How to connect to a MongoDB asynchronously? - javascript

I have a MongoDB database. I'm using Javascript and Node.js (using Mangoose) to access this database. I need to return an array with the name of all the collections in the database. So I use a code that follows.
let connection = mongoose.createConnection(process.env.MONGODB_URI + "/" + dbname);
// Wait 10 seconds for Mongoose to establish the connection.
await new Promise(r => setTimeout(r, 10000));
return connection.db.collections()
.then(stations=>stations.map(stations=>stations.collectionName))
.catch(reason => {
console.error("Error : "+reason);
return null;
});
The code above is working correctly. Now, I'm trying to do the same process asynchronously. For this, I am using the following code.
async function station_list_all(dbname){
return new Promise((resolve,reject) => {
try {
let connection = mongoose.createConnection(process.env.MONGODB_URI + "/" + dbname);
resolve(connection);
}catch(err){
reject(new Error("DB not found!"));
}
})
.then(connection => connection.db)
.then(db => db.collections())
.then(stations=>stations.map(station=>station.collectionName))
.catch(err => {
console.error("Error : "+err);
return null;
});
}
Unfortunately, instead of the collection names, I get the message: Error : TypeError: Cannot read property 'collections' of undefined.
I think db is returning undefined, but... Shouldn't the code wait until db has a valid value?

Try with async await:
try {
await mongoose.connect('mongo url with db name');
// other process
} catch (error) {
handleError(error);
}
Or you can connect using callback:
try {
mongoose.connect('mongo url with db name').then(()=>{
// other process
});
} catch (error) {
handleError(error);
}
In Same way you can try with promise also. There is no need use settimeout.
For More Ref Please visit: Mongoose Connections

Related

Why can't I return data after await a promise object in async function

I'm a newbie studying coding.
I tried to mock mysql database query, so I followed below code(https://techhelpnotes.com/node-js-mock-database-jest-using-ts-jest-utils/)
//database.js
const getDbConnection = () => {
const pool = mysql.createPool(DB_CONFIG);
return {
query : function (sql) {
return util.promisify(pool.query).call(pool, sql);
};
//controller.js
try {
let result = await db.query(sql);
res.status(200).json(result);
} catch (error) {
console.log(DB error, error);
}
It worked for me, but I thought I could return the query with await by using it in databse.js like the one below
query : async function(sql) {
try {
return await util.promisify(pool.query).call(pool, sql);
} catch (error) {
console.log(DB error, error);
}
}
so I thought I can use query function without error handling
let result = await db.query(sql);
But it doesn't work. What difference is there between the two codes that makes the above code work and the below now??
Many thanks!

Error "Given transaction number * does not match" in mongodb and nodejs

I want to modify two schema while adding data. For that I used ACID transaction of mongodb with nodejs as follow. But, when I run program it displays the error like
(node:171072) UnhandledPromiseRejectionWarning: MongoError: Given transaction number 3 does not match any in-progress transactions. The active transaction number is 2
at MessageStream.messageHandler (/home/user/Projects/project/node_modules/mongodb/lib/cmap/connection.js:272:20)
at MessageStream.emit (events.js:375:28)
at MessageStream.emit (domain.js:470:12)
addData = async(request: Request, response: Response) => {
const session = await stockSchema.startSession()
try {
const userData = request.body
let data = {}
const transaction = await session.withTransaction(async() => {
try {
userData.products.map(async(item: any) => {
await inventorySchema.findOneAndUpdate({ _id: item.materialID }, { $inc: {qty: -item.qty }}, { session });
});
data = new stockSchema(userData);
await data.save({ session });
} catch (error) {
await session.abortTransaction()
throw new Error("Could not create data. Try again.");
}
});
if (transaction) {
session.endSession()
return returnData(response, data, 'Data created successfully.');
} else {
throw new Error("Could not create data. Try again.");
}
} catch (error: any) {
session.endSession();
return Error(response, error.message, {});
}
}
So you might have figured out the answer to this already, but anyway, after months of frustration, and no clear answer on the internet, i finally figured it out.
The problem with your code above is that you are passing session into a database operation (the .findOneAndUpdate function above) that is running within .map . Meaning, your 'transaction session' is being used concurrently, which is what is causing the error. read this: https://www.mongodb.com/community/forums/t/concurrency-in-mongodb-transactions/14945 (it explains why concurrency with transactions creates a bit of a mess.)
Anyway, instead of .map, use a recursive function that fires each DB operation one after another rather than concurrently, and all your problems will be solved.
You could use a function something like this:
const executeInQueue = async ({
dataAry, //the array that you .map through
callback, //the function that you fire inside .map
idx = 0,
results = [],
}) => {
if (idx === dataAry.length) return results;
//else if idx !== dataAry.length
let d = dataAry[idx];
try {
let result = await callback(d, idx);
results.push(result);
return executeInQueue({
dataAry,
callback,
log,
idx: idx + 1,
results,
});
} catch (err) {
console.log({ err });
return results.push("error");
}
};

Unable to use async await with sequelize.authenticate() method

The SQL password expires every 24 hour once (The interval might change anytime without prior notice), hence I want to validate the authentication using Sequelize beforeConnect hook before I run a SQL query.
Without async await the SQL query will fail and then authenticate and the preceding queries will pass. I want to run the code async way so no SQL queries fail. Kindly help me fix this problem.
const sequelize = new Sequelize(database, username, password, params);
sequelize.beforeConnect(async (currConfig) => {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
});
You can promisify the beforeConnect
let currConfig = await prepare(sequelize);
try {
await sequelize.authenticate();
console.log("Connection has been established successfully.");
} catch (error) {
console.error("Unable to connect to the database:", error);
}
function prepare(sequelize) {
return new Promise(res => {
sequelize.beforeConnect(config => res(config));
});
}
Just make sure your function has async infront of it

Async function does not wait for await function to end

i have an async function that do not work as expected, here is the code :
const onCreateCoachSession = async (event, context) => {
const { coachSessionID } = context.params;
let coachSession = event.val();
let opentokSessionId = 'prout';
await opentok.createSession({ mediaMode: 'relayed' }, function(
error,
session
) {
if (error) {
console.log('Error creating session:', error);
} else {
opentokSessionId = session.sessionId;
console.log('opentokSessionIdBefore: ', opentokSessionId);
const sessionId = session.sessionId;
console.log('Session ID: ' + sessionId);
coachSession.tokbox = {
archiving: true,
sessionID: sessionId,
sessionIsCreated: true,
};
db.ref(`coachSessions/${coachSessionID}`).update(coachSession);
}
});
console.log('opentokSessionIdEnd: ', opentokSessionId);
};
My function onCreateCoachSession trigger on a firebase event (it's a cloud function), but it does not end for opentok.createSession to end, i don't understand why as i put an await before.
Can anyone have an idea why my code trigger directly the last console log (opentokSessionIdEnd)
Here is a screenshot on order of console.log :
It's probably a simple problem of async/await that i missed but i cannot see what.
I thanks in advance the community for the help.
You're using createSession in callback mode (you're giving it a callback function), so it doesn't return a Promise, so it can't be awaited.
Two solutions :
1/ Use createSession in Promise mode (if it allows this, see the doc)
let session = null;
try{
session = await opentok.createSession({ mediaMode: 'relayed' })
} catch(err) {
console.log('Error creating session:', error);
}
or 2/ await a Promise
let session;
try {
session = await new Promise((resolve, reject) => {
opentok.createSession({ mediaMode: 'relayed' }, (error, session) => {
if (error) {
return reject(error)
}
resolve(session);
})
})
} catch (err) {
console.log('Error creating session:', err);
throw new Error(err);
}
opentokSessionId = session.sessionId;
console.log('opentokSessionIdBefore: ', opentokSessionId);
// ...
await means it will wait till the promise is resolved. I guess there is no promise returned in this case. you can create your own promise and handle the case

How to handle error properly with nested promises in different modules

I'm new to Node JS, and I'm struggling to handle error properly when using promises.
Here is what I currently have:
I have a module call db.js with an init function used to set up and start the db connection:
function initDb(){
return new Promise((resolve, reject) => {
if(database){
console.warn("Trying to init DB again !")
}else{
client.connect(config.db.url, {useNewUrlParser: true, useUnifiedTopology: true})
.then((client) => {
console.log("DB initialized - connected to database: " + config.db.name)
database = client.db(config.db.name)
})
.catch((err) => {
reject(err)
})
}
resolve(database)
})}
This function will return a promise, and it will be called into the index.js:
initDb()
.then(() => {
app.listen(8081, function () {
console.log('App is running on port 8081')
})
})
.catch((error) => {
console.log(error)
})
As you can see I have two catch. One in the db module and the other one in the index.js
It seams weird to catch error in two places... What is the good pattern to use to handle error in this case ?
You'll want to avoid the Promise constructor antipattern. Also don't store the database itself in a variable, store the promise for it instead.
let databasePromise;
function initDb() {
if (databasePromise) {
console.debug("DB already initialised");
return databasePromise;
}
databasePromise = client.connect(config.db.url, {useNewUrlParser: true, useUnifiedTopology: true})
.then((client) => {
console.log("DB initialized - connected to database: " + config.db.name)
return client.db(config.db.name)
});
return databasePromise;
}
If you don't write catch in the initDb function in db.js module, you can catch that error in the calling function, so it's okay if you don't write .catch((err) => { reject(err)}) in db.js module, the error will go to calling function in index.js and you can directly handle it there.
It is not weird to catch the error at both the place, in fact in coming versions of node this will be a recommended practice to write catch(err) for handling errors at all promises.

Categories

Resources