How to use async/await with mongoose - node.js

In node.js I had code like following:
mongoose.connect(dbURI, dbOptions)
.then(() => {
console.log("ok");
},
err => {
console.log('error: '+ err)
}
);
Now i want to do it with async/await syntax. So i could start with var mcResult = await mongoose.connect(dbURI, dbOptions);, afaik it will wait for operation, until it ends with any result (much like calling C function read() or fread() in syncronous mode).
But what should I write then? What does that return to the mcResult variable and how to check for an error or success? Basically I want a similar snippet, but written with proper async/await syntax.
Also I wonder because I have auto reconnect, among dbOptions:
dbOptions: {
autoReconnect: true,
reconnectTries: 999999999,
reconnectInterval: 3000
}
Would it "stuck" on await forever, in case if database connection is unavailble? I hope you can give me a clue on what would happen and how that would work.

Basically I want a similar snippet, but written with proper async/await syntax.
(async () => {
try {
await mongoose.connect(dbURI, dbOptions)
} catch (err) {
console.log('error: ' + err)
}
})()

Please try this, Below code has basics of db connectivity and a query :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let url = 'mongodb://localhost:27017/test';
const usersSchema = new Schema({
any: {}
}, {
strict: false
});
const Users = mongoose.model('users', usersSchema, 'users');
/** We've created schema as in mongoose you need schemas for your collections to do operations on them */
const dbConnect = async () => {
let db = null;
try {
/** In real-time you'll split DB connection(into another file) away from DB calls */
await mongoose.connect(url, { useNewUrlParser: true }); // await on a step makes process to wait until it's done/ err'd out.
db = mongoose.connection;
let dbResp = await Users.find({}).lean(); /** Gets all documents out of users collection.
Using .lean() to convert MongoDB documents to raw Js objects for accessing further. */
db.close(); // Needs to close connection, In general you don't close & re-create often. But needed for test scripts - You might use connection pooling in real-time.
return dbResp;
} catch (err) {
(db) && db.close(); /** Needs to close connection -
Only if mongoose.connect() is success & fails after it, as db connection is established by then. */
console.log('Error at dbConnect ::', err)
throw err;
}
}
dbConnect().then(res => console.log('Printing at callee ::', res)).catch(err => console.log('Err at Call ::', err));
As we're talking about async/await then few things I wanted to mention - await definitely needs it's function to be declared as async - otherwise it would throw an error. And it's recommended to wrap async/await code inside try/catch block.

const connectDb = async () => {
await mongoose.connect(dbUri, dbOptions).then(
() => {
console.info(`Connected to database`)
},
error => {
console.error(`Connection error: ${error.stack}`)
process.exit(1)
}
)
}
connectDb().catch(error => console.error(error))
Lets assume the use of then() is prohibited, you can result to this...
const connectDb = async () => {
try {
await mongoose.connect(dbConfig.url, dbConfigOptions)
console.info(`Connected to database on Worker process: ${process.pid}`)
} catch (error) {
console.error(`Connection error: ${error.stack} on Worker process: ${process.pid}`)
process.exit(1)
}
}

Related

Unhandled promise rejection. Can't connect to server

I was wondering what I'm doing wrong here.I couldn't connect to the server and gives me a message that a promise was rejected and was not handled by catch(). Am i missing an async function somewhere? Thanks in advance.
const mongoose = require("mongoose");
const Dishes = require("./models/dishes");
const url = "mongodb://localhost:27017/conFusion";
const connect = mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
connect.then(db => {
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
newDish
.save()
.then(dish => {
console.log(dish);
return Dishes.find({});
})
.then(dishes => {
console.log(dishes);
return Dishes.remove({});
})
.then(() => {
return mongoose.connection.close();
})
.catch(err => {
console.log(err);
});
});
You have no rejection handler on the promise from mongoose.connect itself (your connect constant). you only use then, not catch, and you don't supply the second argument to then.
So the minimum change is:
connect.then(db => {
// ...
})
.catch(error => { // ***
// ...handle/report error connecting... // ***
}); // ***
Am i missing an async function somewhere?
No, but using one might make the code easier to follow (this is subjective). The purpose of async/await is to make it possible to write code with our usual flow-control structures while using promises.
For instance, if you can't use top-level await or you don't want to, you could wrap all your code in an immediately-invoked async function like this:
const mongoose = require("mongoose");
const Dishes = require("./models/dishes");
const url = "mongodb://localhost:27017/conFusion";
// If you can't use top level `await`, you can use an `async` IIFE
(async () => {
const connect = await mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
const dish = await newDish.save();
console.log(dish);
const dishes = await Dishes.find({});
console.log(dishes);
await Dishes.remove({});
await mongoose.connection.close();
})().catch(error => {
// ...handle/report error...
});
If you can use top-level await and you want to (it does mean you have to switch to using JavaScript's own module syntax instead of CJS modules, but IMHO that's a good thing anyway), you can do this:
import mongoose from "mongoose";
import Dishes from "./models/dishes.js";
const url = "mongodb://localhost:27017/conFusion";
try {
const connect = await mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
const dish = await newDish.save();
console.log(dish);
const dishes = await Dishes.find({});
console.log(dishes);
await Dishes.remove({});
await mongoose.connection.close();
} catch (error) {
// ...handle/report error...
}
Note, though, that you'd probably only want to do that in your top-level module (which it looks like this is), since it holds up resolution of the module tree. That's fine for the entry point module, or a module that can't create its exports until a promise settles, but for the logic in the code you've shown it would probably only be appropriate in the entry point module.

mongoose async await, unable to capture connection state

I'm trying to capture the connection success state for mongoose/mongodb.
The idea is simply that, when I start the app, if the connection fails, I need an alert recorded.
I can't figure out why, once I connect - or once the connection fails - the function does not return what I tell it to return.
import { DBURL } from '../parameters/environment';
const mongoose = require('mongoose');
const chalk = require('chalk');
const connected = chalk.bold.cyan;
const error = chalk.bold.yellow;
const connectMe = async () => {
await mongoose.connect(DBURL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
console.log(connected('DB connection successful'));
return 'Success';
})
.catch((reason) => {
console.log(error('Unable to connect to the mongodb instance. Error: '), reason);
return 'FAIL';
});
return 'Why am I returning this????';
};
module.exports = connectMe;
I simply call it and try to display the result.
But regardless of the DB state, the return statement in .then or .catch is ignored
Server.js:
const connectMe = require('./db-actions/db-connect');
const myResult = connectMe();
myResult.then(x => console.log(x));
If mongodb is up, I get this:
DB connection successful
Why am I returning this????
If mongodb is down, I get this:
Unable to connect to the mongodb instance. Error: MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:30000
Why am I returning this????
Console.log works, but the return does not.
Any idea why?
Why it seems return is not working:
You are combining both async/await and .then/.catch, you should choose one of the two. Also, the return keyword is not properly placed within connectMe the function.
The fix:
Since, you are expecting the connectMe function to return a promise so you can attach a .then like this:
myResult.then(x => console.log(x));
You can just go with a .then/.catch in the connectMe function and the return statement should be on mongoose.connect, that's the promise the outside world(i.e outside the function) needs to interact with. Code:
const connectMe = () => {
return mongoose.connect(DBURL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
console.log(connected('DB connection successful'));
return 'Success';
})
.catch ((reason) => {
console.log(error('Unable to connect to the mongodb instance. Error: '), reason);
return 'FAIL';
})
};

How to await a streaming sql query in node.js

I need to call out to a function that runs a sql query, with row level functionality, and await the entire process before continuing.
Function code:
const sql = require('mssql')
exports.doit = ()=>{
const pool1 = new sql.ConnectionPool(dbConfig);
const pool1Connect = pool1.connect();
pool1.on('error', err => {
console.error('error occurred on pool')
})
await pool1Connect
try {
const request = pool1.request();
request.stream = true;
request.query('select * from dbo.user');
request.on('row', async orow => {
console.log('outer row');
const innerPool = new sql.ConnectionPool(dbConfig);
const innerConnection = innerPool.connect();
innerPool.on('error', err => {
console.error('error occurred on pool')
});
const iConnection = await innerConnection;
connections.push(iConnection);
const innerRequest = innerPool.request();
innerRequest.stream = true;
var iquery = 'select * from dbo.order where userId='+ orow.userId
innerRequest.query(iquery);
innerRequest.on('row', async irow => {
console.log(`User: ${orow.userId} Order: ${irow.orderId}`);
});
innerRequest.on('done', async () => {
console.log('inner done');
iConnection.close();
});
});
request.on('done', async () => {
console.log('outer done');
})
} catch (err) {
console.error('SQL error', err);
}
sql.on('error', err => {
// ... error handler
})
}
Then call the above function like this:
var doit = require('./testmeHandler.js').doit;
doit()
.then(()=>{
console.log("I AM DONE");
});
OR
await doit();
console.log('I AM DONE');
You get the idea...
But what is really happening is, the function gets called, then 'I AM DONE' and then the results of all the sql calls.
Can someone help me get 'I AM DONE' at the bottom? Still getting used to the async/await and promises.
Thanks
After quite a bit of time trying to get this to work synchronously from the caller I gave up and re-wrote the method to use the regular query (not streaming) and implemented my own paging/throttling as to control memory usage. It works great now!
I am using a connection pool to allow for sub queries and other processes to occur async within a batch of results.
I will post the updated code.
Somehow I believe you have jumbled it all up a bit.
Use this
exports.doit = async ()=>
{
const request = new sql.Request(conn)
let records = await request.query('select * from dbo.user')
records.forEach(async r=>{
try{
// do something
const inner = new sql.Request(conn)
let recordInner = await request.query(innerQuery)
recordInner.forEach(async r=>{//do some stuff})
inner.close()
}
catch(err){
//do something with the error
}
records.close()
})
}
The execution:
async execute(){
const result = await doit()
return result
}
execute()
Though I have no idea why you are using two connections at all . Just try writing a more defined query using JOIN OR WHERE subquery. You can achieve all this in a single query instead of a using nested connection. SQL though a bit old, it really is quite powerful.
select * from dbo.order WHERE userId IN (SELECT userId FROM dbo.user)
Makes more sense to me. But, whatever floats your boat.
More on sub-queries: https://www.dofactory.com/sql/subquery

How to restore mongo in chai mocha

So I want to restore mongo database before tests begin.
I do this way:
const app = require("../app");
const chai = require("chai");
const mongoose = require("mongoose");
const User = require('../models/users');
const Region = require('../models/regions');
const testUsers = require('../testdata/users.json');
const testRegions = require('../testdata/regions.json');
describe('Restoring database', function () {
before(function(done) {
var promises = [
User.deleteMany().exec()
,Region.deleteMany().exec()
];
console.log('Cleaned database');
done();
});
before(function(done) {
testUsers.users.forEach(element => {
var ObjectId = mongoose.Types.ObjectId;
element._id = new ObjectId(element._id);
var newUser = new User(element);
newUser.save(function (err, result) {
if (err) {
console.log("err:",err);
}
});
});
console.log('Users added');
done();
});
before(function(done) {
testRegions.regions.forEach(element => {
var newRegion = new Region(element);
newRegion.save(function (err, result) {
if (err) {
console.log("err:",err);
}
});
});
console.log('Regions added');
done();
});
testdata/users.json and testdata/regions.json are simple json files including key/pair values.
Does this look good?
When I run
npm test
It does not give any error. And in the console I see this:
Restoring database
Cleaned database
Users added
Regions added
But when I look in database I get different results.
Sometimes everything looks good. All the rows are in the collections.
Sometimes a few rows are missing in one of the collections.
Sometimes one of the collections is empty.
This is a very strange behaviour.
I also tried to add in the variable "promises" each of the "newUser" and "newRegion" instead of executing them directly.
But I still get these strange results.
Whats the deal?
The issue is due to done() being called before the async statements/promises have completed.
Either use async/await or use Promises and only call done() when your async statements/promises have completed.
For example:
No done() call as we using await statements which will wait till each statement completes before continuing:
before(async function() {
let userResult = await User.deleteMany();
let regionResult = wait Region.deleteMany();
console.log('Cleaned database');
});
Or use done() with promises:
before(function(done) {
User.deleteMany()
.then(result => {
console.log('Cleaned database');
done();
});
});
The syntax in your before example is not adding Promises at all, it is simply adding those functions to an array:
var promises = [
User.deleteMany().exec()
,Region.deleteMany().exec()
];
Take a look at the following related answer to help.

Test Node.js API with Jest (and mockgoose)

Two questions here:
1) Is Jest a good options to test Node.js (express) APIs?
2) I'm trying to use Jest with Mockgoose, but I can't figure out how to establish the connection and run tests after. Here is my final attempt before coming on SO:
const Mongoose = require('mongoose').Mongoose
const mongoose = new Mongoose()
mongoose.Promise = require('bluebird')
const mockgoose = require('mockgoose')
const connectDB = (cb) => () => {
return mockgoose(mongoose).then(() => {
return mongoose.connect('mongodb://test/testingDB', err => {
if (err) {
console.log('err is', err)
return process.exit()
}
return cb(() => {
console.log('END') // this is logged
mongoose.connection.close()
})
})
})
}
describe('test api', connectDB((end) => {
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3)
})
end()
}))
The error is Your test suite must contain at least one test. This error makes a bit of sense to me but I can't figure out how to solve it. Any suggestions?
Output:
Test suite failed to run
Your test suite must contain at least one test.
Very late answer, but I hope it'll help.
If you pay attention, your describe block has no test function inside it.
The test function is actually inside the callback passed to describe.. kind of, the stack is complicated due to arrow function callbacks.
this example code will generate the same problem..
describe('tests',function(){
function cb() {
setTimeout(function(){
it('this does not work',function(end){
end();
});
},500);
}
cb();
setTimeout(function(){
it('also does not work',function(end){
end();
});
},500);
});
since the connection to mongo is async, when jest first scans the function to find "tests" inside the describe, it fails as there is none.
it may not look like it, but that's exactly what you're doing.
I think in this case your solution was a bit too clever(to the point it doesn't work), and breaking it down to simpler statements could've helped pinpointing this issue
var mongoose = require('mongoose');
// mongoose.connect('mongodb://localhost/animal', { useNewUrlParser: true });
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
var kittySchema = new mongoose.Schema({
name: String
});
var god = mongoose.model('god', kittySchema);
module.exports = god;
code for god.js file
describe("post api", () => {
//life cycle hooks for unit testing
beforeAll(() => {
mongoose.connect(
"mongodb://localhost/animal",
{ useNewUrlParser: true }
);
});
//test the api functions
test("post the data", async () => {
console.log("inside the test data ");
var silence = await new god({ name: "bigboss" }).save();
});
// disconnecting the mongoose connections
afterAll(() => {
// mongoose.connection.db.dropDatabase(function (err) {
// console.log('db dropped');
// // process.exit(0);
// });
mongoose.disconnect(done);
});
});
Jest testing code ..using jest we can we store the name in db

Resources