Mongoose 5 async connection trouble - node.js

According to this, the proper way for handling errors during initial connection is this:
try {
await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
} catch (error) {
handleError(error);
}
Unfortunately, this results in a SyntaxError:
await mongoose.connect(mongoConnectionString, { useNewUrlParser: true });
^^^^^^^
SyntaxError: Unexpected identifier
When I remove await, the code is executed without any issues. When I googled SyntaxError: Unexpected identifier with await, I found that this happens when await is used with a not async function. But this doesn't make any sense since Mongoose is using asnyc connect from version 5 on. Also, the following async code is working properly as well:
mongoose.connect(mongoConnectionString, {
useNewUrlParser: true
})
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
});
The dependency in package.json:
"mongoose": "^5.6.0"
index.js:
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const mongoConnectionString = `mongodb://${process.env.MONGO_DB_USER}:${process.env.MONGO_DB_PW}#mongodb4711:27017/visitor-ips`;
initialMongoConnect(mongoose, mongoConnectionString);
function initialMongoConnect(mongoose, mongoConnectionString) {
try {
await mongoose.connect(mongoConnectionString, { useNewUrlParser: true });
} catch (error) {
handleError(error, mongoose, mongoConnectionString);
}
console.log('Initial connect to MongoDB successful');
}
function handleError(error, mongoose, mongoConnectionString) {
console.log('Error on initial connection: ' + error);
// wait 3 seconds and try to do the initial connect again
setTimeout(function() {initialMongoConnect(mongoose, mongoConnectionString)}, 3000);
}
So what's the issue with await here?

mongoose async operations, it supports promise runs async by default it doesn't need to await function because it is async when its success used then (()=>"what should do when it connection ) if it error use catch(()=>"what happened when the error happened ) and you should use keyword to used await that is async in start the function that wants to run as async function example
const a=async function(){
await ///what it is await the need to domming

Related

mongoose not connected to database

why i should commmented the code
await mongoose.connection.close()
to make it work ?
here is my code:
const { MongoClient } = require("mongodb");
const mongoose = require('mongoose');
require('dotenv').config();
async function main() {
const uri = process.env.MONGO_URL;
try {
// Connect to the MongoDB cluster
await mongoose.connect(uri)
.then(()=> console.log("connect Succes"))
.catch((err) => {console.error(err)});
await createListing();
} finally {
// Close the connection to the MongoDB cluster
await mongoose.connection.close()
console.log("connect Closed")
}
}
main().catch(console.error);
async function createListing() {
const piscSchema = new mongoose.Schema({
name: String,
date: { type: Date, default: Date.now },
pool: String,
point: Number,
isAdd:Boolean
});
const piscModel = mongoose.model('piscModel', piscSchema, 'PointPiscine');
var itemPisc = new piscModel({
date:"12.10.2022",
pool:"dsfs",
point: 70,
isAdd:false
});
itemPisc.save(function (err, res) {
if (err) console.error(err);
console.log(res)
});
console.log("fin function call")
}
when i am not commented the code that close the connection.
i got this message
it is strange because it is connected to my mongodb.
as you can see the console log:
connect Succes
fin function call
connect Closed
You are calling the following function using a callback
itemPisc.save(function (err, res) {
if (err) console.error(err);
console.log(res)
});
This way the code continues to run without waiting for the result of this operation. It will then close the database connection without waiting for the result of this save function, which leads to this error.
If you modify you function the following way, it should wait for the result and close the connection afterwards:
try {
console.log(await itemPisc.save());
} catch (err) {
console.log(err);
}
You see the function 'main' is asynchronous, all those async functions are called asynchronously. You can try calling it with .then and do everything else in that .then:
... All the previous code
main().then(()=> {
async function createListing() {
...
// All the other code
})
Please comment on this line await mongoose.connection.close() then try I hope work it perfectly!!

Question about the syntax of a function in Node.js

I am following a tutorial to make a blog, and for the MongoDB connection in the server.js file, the instructor made a boiler connection function withDB. Operations and res are props of withDB function. In line 6, is operations a function passed a prop of the withDB functions?
Below is the withDB function.
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('my-blog');
await operations(db); // is operations a function that takes db as its props?
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error });
}
}
Using withDB in a function
app.get('/api/articles/:name', async (req, res) => {
withDB(async (db) => {
const articleName = req.params.name;
const articleInfo = await db.collection('articles').findOne({ name: articleName })
res.status(200).json(articleInfo);
}, res);
})
yes actually operations is your callback function, you call it with db as param once you initialize your database connection.
Maybe you're not comfortable with ES6 arrow function syntax. you can find in Mdn doc a simple example with old regular function, and in your case it could be :
function findArticlesByName(articleName) {
return function(db) {
return db.collection('articles').findOne({ name:
articleName });
}
}
async function withDB(callback) {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('my-blog');
return callback(db);
} catch (error) {
throw new Error({ message: 'Error connecting to db', error });
} finally {
client?.close();
}
}
app.get('/api/articles/:name', async (req, res) => {
try {
const articleInfo = await withDB(findArticlesByName(req.params.name));
res.status(200).json(articleInfo);
} catch(error) {
res.status(500).json(error);
}
})
Conclusion, you could easily inline your callback function like in your example, but maybe it's more understandable that way.
Moreover, you should avoid to use a wrapper in order to create and close your db connection after each request, because it could occur some weird errors. Databases connections or any resource-intensive tasks should be shared as much as possible.
So a better solution is to create a specific class with the default implementation of your singleton, construct a single instance of your connection at the top of your app, pass the single instance into each module that needs it then close your connection just before exiting your app.
Hope it'll help.

Jest + Mongoose: Have to run find twice to get results

I am building tests for my node/express controller methods and using #shelf/jest-mongodb. I am creating a document first, and then when I try to find that I have to run find twice from model in order to get the results. It should get the results in the first find instead.
test.js
const { Subscription } = require('../src/models/subscription.schemaModel'); // model
const {
createSubscription,
} = require('../src/controllers/subscription.controller');
const subData = {...};
beforeAll(async () => {
await mongoose.connect(
process.env.MONGO_URL,
{ useNewUrlParser: true, useUnifiedTopology: true },
(err) => {
if (err) {
console.error(err);
process.exit(1);
}
}
);
});
afterAll(async () => {
await mongoose.connection.close();
});
describe('creates a subscription ', () => {
it('can be created correctly', async () => {
const sub = await createSubscription(subData);
await Subscription.find(); // if I comment out this line, I would get 0 results.
const subs = await Subscription.find();
expect(subs[0].items[0].sku).toBe(233234);
});
});
subscription.controller.js
const Mongoose = require('mongoose');
const { Subscription } = require('../models/subscription.schemaModel');
const isTestEnv = process.env.NODE_ENV === 'test';
module.exports.createSubscription = async (data) => {
try {
let error = null;
const doc = new Subscription(data);
doc.accountId = Mongoose.Types.ObjectId(doc.accountId);
await doc.save(function (err) {
if (err) {
logger.error(`createSubscription saving ${err}`);
error = err;
}
});
if (!error) {
logger.info(
`Subscription created => id: ${doc._id} store: ${doc.store}`
);
return doc;
} else {
return error;
}
} catch (err) {
logger.error(`createSubscription ${err}`);
}
};
The schemaModel file essentially contains the schema and exports model. Everything seems to work fine if I would do all the operations in the test file (schema+model+controller module)which defeats the purpose of testing my modules but not if I am importing. In this case I would have to run find() twice to get the results.
I have been trying multiple things from what I could find from googling, but no luck! Any help or lead would be appreciated. Also let me know if you need any other details.
Thank you!!
The only problem that posted code contains is that Mongoose promise API is mixed with legacy callback API. It appears that save results in race condition that is has been circumvented by random delay that extra find provides.
Although Mongoose documentation mentions that methods unconditionally return promises, a common pattern for JavaScript APIs that support both promises and callbacks is to enable promise control flow by omitting callback argument, and vice versa. This is most likely what happens here.
A way to avoid race conditions in such cases is to stick to promise control flow, e.g.:
beforeAll(async () => {
try {
await mongoose.connect(
process.env.MONGO_URL,
{ useNewUrlParser: true, useUnifiedTopology: true },
)
} catch (err) {
console.error(err);
process.exit(1);
}
});

How can I access the result from a promise inside another?

I am playing around with promises, I have the following code to access my mongodb:
MongoClient.connect(url, { useUnifiedTopology: true })
.then(client => {
const db = client.db(dbName);
return db.collection('dogs');
})
.then(collection => collection.find().toArray())
.then(array => console.log(array))
// Client is not defined, how do I access it?
.finally(() => client.close())
.catch(error => {
console.log(error);
});
I can't access the client inside finally. Is there a good pattern to achieve this?
You can either use async/await:
const client = await MongoClient.connect(url, { useUnifiedTopology: true })
const db = client.db(dbName);
const dogs = db.collection('dogs');
...
When you have operations like find you can await them or using then as you did. If you are not very confident with promises, asyn/await might be clearer for you.
You can also await chained promises with then...
With async/await you can use try/catch/finally, that might be an easier solution for you.
You can wrap your code with an async function in order to use await, and create a variable before the try and catch to save the client, the code will be as follows:
const functionName = async () => {
let client;
try {
client = await MongoClient.connect(url, { useUnifiedTopology: true });
const db = client.db(dbName);
const collection = db.collection("dogs");
const array = collection.find().toArray();
console.log(array);
} catch (error) {
console.log(error);
} finally {
client.close();
}
};

Type Error in Promisifying Mongoose Connect

I tried to Promisify Mongoose connect by using bluebird, I need to reduce my callbacks so I used bluebird.But It gives me the error below.
var expect = require('chai').expect;
var mongoose = require('mongoose');
var jobModel = require('../models/job');
var Promise = require('bluebird');
function resetJobs() {
return new Promise(function(resolve, reject) {
mongoose.connection.collections['jobs'].drop(resolve, reject);
});
};
function findJobs(query) {
return Promise.cast(mongoose.model('Job').find({}).exec());
};
var connectDB = Promise.promisify(mongoose.connect,mongoose);
describe('get jobs', function() {
it('Should not be empty since jobs are seeded', function(done) {
connectDB('mongodb://localhost/jobfinder').then(function() {
resetJobs()
.then(jobModel.seedJobs)
.then(findJobs).then(function(jobList) {
expect(jobList.length).to.be.at.least(1);
done();
});
});
});
});
But this gives me a error
Unhandled rejection TypeError: Cannot read property 'connection' of undefined
at Mongoose.connect (F:\MyProjects\JobFinder\node_modules\mongoose\lib\index.js:232:18)
at tryCatcher (F:\MyProjects\JobFinder\node_modules\bluebird\js\release\util.js:11:23)
at ret (eval at <anonymous> (F:\MyProjects\JobFinder\node_modules\bluebird\js\release\promisify.js:184:12), <anonymous>:14:23)
at Context.<anonymous> (F:\MyProjects\JobFinder\test\jobs-data-spec.js:22:3)
The versions of packages I'm using as follows
"bluebird": "^3.1.1",
"express": "^4.13.4",
"mongoose": "^4.3.6"
I am working on the same tutorial.
Bluebird changes the api in 3.0 from
// 2.x Promise.promisify(fn, ctx);
// 3.0 Promise.promisify(fn, {context: ctx});
I made the call change and the calls stopped throwing the errors.
See here for the Bluebird explanation:
http://bluebirdjs.com/docs/new-in-bluebird-3.html
Hope this helps
The following code seems to works for me.
var connectMongoose = Promise.promisify(mongoose.connect, {context: mongoose});
connectMongoose('MONGO_URL', mongoose)
.then(..)
I'm working on the same tutorial as well, and here is what I did to resolve the issue.
npm uninstall bluebird
npm install --save bluebird#2.0
Then when you run your tests with mocha you should pass.
You can also manually promisify mongoose.connect, without using bluebird:
const mongoose = require('mongoose');
const { promisify } = require('util');
const connectMongoose = promisify((resolve, reject) => {
const options = { useNewUrlParser: true, useUnifiedTopology: true };
const uri = 'mongodb+srv://<USERNAME>:<PASSWORD>' +
'#cluster0.iz4o8.mongodb.net/<DB-NAME>?retryWrites=true&w=majority';
try {
mongoose.connect(uri, options, resolve);
} catch (err) {
reject(err);
}
});
(async () => {
try {
await connectMongoose();
console.log('connected to monoose! :)');
}
catch (err) {
console.error('could not connect to mongoose :(', err);
process.exit(-1);
}
})();

Resources