2
votes

I realize this may seem like a duplicate of other questions, but I have looked at every suggested SO question I could find before posting this, and I am looking for help on this specific scenario, as none of the other answers have worked for me.

I have a Node/Express app that is initializing a single MongoDB connection to be used by a REST API. The first step is to connect to the MongoDB instance. If the initial connection fails, it will throw an error as expected. I am using async/await with a try/catch block inside of it to handle that. Everywhere I have looked says that this should be sufficient to catch these async/await promise rejections, but I keep getting an error about an UnhandledPromiseRejection no matter where I throw in a .catch() or try/catch for my code (as suggested in other SO posts).

In this link, for instance, I have pretty much the same thing that is described in the error handling section, but the problem still exists.

https://javascript.info/async-await

Here's the error (I know what is causing the error itself - I have the MongoDB service stopped right now - but I am trying to fix the unhandled promise rejection error):

(node:15633) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:15633) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. (node:13802) UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [localhost:27017] on first connect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017] at Pool.<anonymous> (/home/allen/scripts/lysi/eosMain/node_modules/mongodb-core/lib/topologies/server.js:562:11) at Pool.emit (events.js:189:13) at Connection.<anonymous> (/home/allen/scripts/lysi/eosMain/node_modules/mongodb-core/lib/connection/pool.js:316:12) at Object.onceWrapper (events.js:277:13) at Connection.emit (events.js:189:13) at Socket.<anonymous> (/home/allen/scripts/lysi/eosMain/node_modules/mongodb-core/lib/connection/connection.js:245:50) at Object.onceWrapper (events.js:277:13) at Socket.emit (events.js:189:13) at emitErrorNT (internal/streams/destroy.js:82:8) at emitErrorAndCloseNT (internal/streams/destroy.js:50:3) at process._tickCallback (internal/process/next_tick.js:63:19)

and here's my code:

exports.mongoConnect = async (dbName, archiveDbName, userName, password) => {

    // Auth params
    const user = encodeURIComponent(userName);
    const pass = encodeURIComponent(password);
    const authMechanism = 'DEFAULT';

    // Connection URL
    const url = `mongodb://${user}:${pass}@localhost:27017?authMechanism=${authMechanism}&authSource=admin`;
    let client;

    try {
        // Use connect method to connect to the Server
        client = await MongoClient.connect(url, { useNewUrlParser: true, poolSize: 10, autoReconnect: true, reconnectTries: 6, reconnectInterval: 10000 }).catch((e) => { console.error(e) });

        db = client.db(dbName);
        archiveDb = client.db(archiveDbName);

        console.log(`Succesfully connected to the MongoDb instance at URL: mongodb://localhost:27017/ with username: "` + client.s.options.user + `"`);
        console.log(`Succesfully created a MongoDb database instance for database: "` + db.databaseName + `" at URL: mongodb://localhost:27017/`);
        console.log(`Succesfully created a MongoDb database instance for database: "` + archiveDb.databaseName + `" at URL: mongodb://localhost:27017/`);
    } catch (err) {
        console.log(`Error connecting to the MongoDb database at URL: mongodb://localhost:27017/` + dbName);
    }
}

that is being called from app.js like this:

mongoUtil.mongoConnect('myDb', 'myArchiveDb', 'myUser', 'myPassword');

I even tried putting that line in a try/catch block, or adding the promise-style .catch() onto the end of it, with no change.

I can't seem to figure out why it's still complaining about not handling the promise rejection.

EDIT:

Here's the whole app.js file:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var cors = require('cors');
var app = express();

const MongoClient = require('mongodb').MongoClient;
// This is where the mongo connection happens
var mongoUtil = require( './services/mongoUtil' );
var bluebird = require('bluebird');

const jwt = require('./helpers/jwt');

var api = require('./routes/api.route')

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(cors());
app.use(logger('dev'));
app.use(express.json()); 
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/api', api);

// use JWT auth to secure the api
app.use(jwt());

app.use('/users', require('./users/users.controller'));

MongoClient.Promise = bluebird

mongoUtil.mongoConnect('myDb', 'myArchiveDb', 'username', 'password');

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  next();
});

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;`
2
can you make sure your MongoDB server is running? - molamk
It is not currently running, but that's not really the problem I was asking about. This started when I accidentally tried to start my app without MongoDB running. It threw this error so I wanted to investigate what caused it, because it seems like I am not properly handling the error that occurs when it cannot connect to MongoDB. The actual problem is solved by starting MongoDB, but that doesn't fix the fact that promise rejections aren't being handled correctly. - NellaGnute
ah ok! got you. Why not use the Promise notation? MongoClient.connect(...).then(() => { // all good }).catch((error) => { // error here }) - molamk
He also said he already tried that, all is in the question ... @NellaGnute: Can't see anything obviously wrong in your code, will upvote and wait for a more knowledgeable person to answer as I am curious! - MadWard
Does MongoClient.connect even return a Promise? - Rashomon

2 Answers

0
votes

I tested your code and it works fine as you can see in the screen shots below. I think the problem lies with whatever is calling mongoConnect()

Connecting on wrong port Successful connection

0
votes

You are calling connect from app.js in a sync way, So your async/await methods wont work. You can test it using a sleep function:

let sleep = () => {
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
      console.log('Finished sleeping');
      resolve();
    }, 2000)
  })
}

exports.mongoConnect = async (dbName, archiveDbName, userName, password) => {
  await sleep() // sleep two seconds
  // rest of your mongoConnect code
})

Then add a log in app.js after the connection:

mongoUtil.mongoConnect('myDb', 'myArchiveDb', 'username', 'password');
console.log('Finished connection');

You will obtain the following output in console:

Finished connection // You can see the code doesnt wait to mongo to connect
[After 2 seconds]
Finished sleeping // After this sleep log it will start connecting...

To solve this you would need to execute app.js in an async way:

Be advice top-level async functions are not recommended, but I want you to see the error

(async () => {
    // rest of your app.js
    await mongoUtil.mongoConnect('myDb', 'myArchiveDb', 'username', 'password');
    console.log('Finished connection attempt');
    // rest of your app.js
})()

And then the error Im getting at console is (No warnings!):

{ MongoNetworkError: failed to connect to server [localhost:27017] on first connect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017]
    at Pool.<anonymous> (/node_modules/mongodb-core/lib/topologies/server.js:564:11)
    at Pool.emit (events.js:182:13)
    at Connection.<anonymous> (/node_modules/mongodb-core/lib/connection/pool.js:317:12)
    at Object.onceWrapper (events.js:273:13)
    at Connection.emit (events.js:182:13)
    at Socket.<anonymous> (/node_modules/mongodb-core/lib/connection/connection.js:246:50)
    at Object.onceWrapper (events.js:273:13)
    at Socket.emit (events.js:182:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
  name: 'MongoNetworkError',
  errorLabels: [ 'TransientTransactionError' ],
  [Symbol(mongoErrorContextSymbol)]: {} }
Error connecting to the MongoDb database at URL: mongodb://localhost:27017/myDb
Finished connection attempt