2

I want my nodejs app to not continue unless it connects to mongodb.

I tried:

//Mongo
async function mongoConnect(mongoDB) {
    var mongoConnectionSuccessful = false;
    LOG("Connecting to mongodb at " + mongoDB + "...");
    while (!mongoConnectionSuccessful) {
        try {
            await mongoose.connect(mongoDB, { useNewUrlParser: true, useUnifiedTopology: true });
            LOG("connected!");
            mongoConnectionSuccessful = true;
            mongoose.connection.on('error', ()=>LOG('MongoDB connection error:'));
            return;
        } catch (error) {
            LOG(error);
        }
        await utils.sleep(500);
    }
}
mongoConnect(config.mongoUrl);

but in order to use await in mongoose.connect, I must make mongConnect async, but I then cannot call it in a blocking way from the code because in order to call like this, I must call with await, but await is only permitted inside async functions.

4
  • If a function block contains an await then the function has to be async. What you need to do is await mongoConnect and put the code that follows after the function call, that way the that following code will "wait" for the promise to be fulfilled before executing. Commented Feb 23, 2020 at 20:03
  • Just call it inside an async function. Needing to be inside a function to be able to call a function is nothing new. We do it in C/C++ all the time. In Java it's even worse as you cannot even define a function outside a class. Just create an async function called main and call main() at the end of your script to start your program Commented Feb 23, 2020 at 20:13
  • @slebetman but I did exactly that. mongoConnect is my main. But when I call mongoConnect, code continues, does not await Commented Feb 23, 2020 at 20:14
  • But mongoConnect is not your main as it is too small to be your entire program. mongoConnect does not even make database queries Commented Feb 23, 2020 at 20:16

4 Answers 4

4

I must call with await, but await is only permitted inside async functions

That's correct. So do just that:

async function main () {
    await mongoConnect(config.mongoUrl);

    // rest of your code...
}

main();

For example if this is an Express server do something like:

const express = require('express');
const app = express();

async function main () {
    await mongoConnect(config.montoUrl);

    const routes = require('./my-routes');

    app.use(routes);
    app.listen(config.port);
}

main();

You don't need to await the main() function (indeed normally you cannot because it is not in a function marked with async) because there is no other javascript code after it. It is at the end of the file.

If you want to run some code after your program ends or catch any uncaught async errors you can all main with a .then() and .catch():

main()
    .then(() => console.log('done'))
    .catch((err) => console.error(err));
Sign up to request clarification or add additional context in comments.

3 Comments

I don't know why you forgot to mention that you have to 'await main()'. Otherwise, the code will simply move on to the next line without waiting.
@Jay You can't do that unless you start node with the --experimental-top-level-await flag. In order to await main you have to call it an another function eg. async function main2 () {await main()} and then call main2(). If you want to await main2() you have to call it in another function (eg main3()). At some point you will have to call a main function (eg main27()) without await. For me personally I'd just call main() without await.
I see. I am a .net developer and all this node js stuff is new. I have made note of the additional notes that you have provided. also, I did not turn on any flags but the node js seems to wait, after I put the await keyword. So, I don't know. again, I am new, so, not sure what is going on :)
0

You want to attempt reconnect if mongoose fails to connect. Here is an example logic without helper lib.Props to the guy who posted this solution in a github issue for mongoose. Here

function createConnection (dbURL, options) {
    var db = mongoose.createConnection(dbURL, options);

    db.on('error', function (err) {
        // If first connect fails because mongod is down, try again later.
        // This is only needed for first connect, not for runtime reconnects.
        // See: https://github.com/Automattic/mongoose/issues/5169
        if (err.message && err.message.match(/failed to connect to server .* on first connect/)) {
            console.log(new Date(), String(err));

            // Wait for a bit, then try to connect again
            setTimeout(function () {
                console.log("Retrying first connect...");
                db.openUri(dbURL).catch(() => {});
                // Why the empty catch?
                // Well, errors thrown by db.open() will also be passed to .on('error'),
                // so we can handle them there, no need to log anything in the catch here.
                // But we still need this empty catch to avoid unhandled rejections.
            }, 20 * 1000);
        } else {
            // Some other error occurred.  Log it.
            console.error(new Date(), String(err));
        }
    });

    db.once('open', function () {
        console.log("Connection to db established.");
    });

    return db;
}

// Use it like
var db = createConnection('mongodb://...', options);

and with a lib promise-retry

const promiseRetry = require('promise-retry')

const options = {
  useNewUrlParser: true,
  reconnectTries: 60,
  reconnectInterval: 1000,
  poolSize: 10,
  bufferMaxEntries: 0 // If not connected, return errors immediately rather than waiting for reconnect
}

const promiseRetryOptions = {
  retries: options.reconnectTries,
  factor: 2,
  minTimeout: options.reconnectInterval,
  maxTimeout: 5000
}

const connect = () => {
  return promiseRetry((retry, number) => {
    logger.info(`MongoClient connecting to ${url} - retry number: ${number}`)
    return MongoClient.connect(url, options).catch(retry)
  }, promiseRetryOptions)
}

module.exports = { connect }

Comments

0

I found the solution in this article

I beleive what you need is to promise your call of mongoConnect(config.mongoUrl),
than await promises until it is called back.

async function getConcurrently() { 
    let promises = []; 
    promises.push(mongoConnect(config.mongoUrl))
    // promises.push(getUsers()); 
    // promises.push(getCategories()); 
    // promises.push(getProducts());

    let mongo = await Promise.all(promises);
    //let [users, categories, products] = await Promise.all(promises); 
}

Please Note this warning in the article:

As the first example, first we create an array of Promises (each one of the get functions are a Promise). Then, we execute all of them concurrently and simultaneously, awaiting for all of them to finish (await Promise.all). Finally, we assign the results to the respective variables users, categories and products. Despite the fact that it works, it’s important to say that using Promises.all() for everything is a bad idea.

Comments

0

I assume that for some reason you have incoming events meanwhile you are attempting to connect to mongo. My approach would be preventing any incoming events (such as starting the server and setting listeners) before connection to mongo. If it's not possible, one way to do it is to use a self-invoking function.

//Mongo
async function mongoConnect(mongoDB) {
    var mongoConnectionSuccessful = false;
    LOG("Connecting to mongodb at " + mongoDB + "...");
    while (!mongoConnectionSuccessful) {
        try {
            await mongoose.connect(mongoDB, { useNewUrlParser: true, useUnifiedTopology: true });
            LOG("connected!");
            mongoConnectionSuccessful = true;
            mongoose.connection.on('error', ()=>LOG('MongoDB connection error:'));
            return;
        } catch (error) {
            LOG(error);
        }
        await utils.sleep(500);
    }
}

(async function() {
  // do stuff
  await mongoConnect(config.mongoUrl);
  // do other stuff
})();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.