DEV Community

loading...

How to initialize a Singleton mongo connection with expressjs

perigk profile image Periklis Gkolias ・1 min read

I am trying to initialize a mongo Singleton connection with expressjs in the app/server.js file, in an async manner. I want to use the native driver and not Mongoose or something similar.

The whole internet is filled with bad patterns (IMHO) that open a new connection with each request.

Has anyone tried and succeeded in the former? Any link or advise will be appreciated.

For example

// app.js
let client = await initMongoConnection()

Discussion

pic
Editor guide
Collapse
avalander profile image
Avalander

What I do is initialize the database connection, plus any other asynchronous initialization I need to do, and then bootstrap the entire application and send the connection to any component that needs it.

So basically what your snippet is hinting at:

initDatabase()
  .then(startApp)

const startApp = db => {
  const app = express()
  app.use('/', makeApi(db))
  app.listen(PORT, () => console.log(`Server started on port ${PORT}`))
}

You can check this project I built a while ago for a complete example.

Collapse
perigk profile image
Periklis Gkolias Author

Your approach looks very interesting. And cleverly simple as hell. I was trying to do it strictly with await calling anonymous functions on the fly, but this looks very promising :)

Thanks a lot

Collapse
avalander profile image
Avalander

Glad it helped :)

this looks very promising

I hope this was an intended pun :D

Thread Thread
perigk profile image
Periklis Gkolias Author

hahaha, no I just noticed what I did :D

Collapse
aramix profile image
Aram Bayadyan

if you are using mongoose then you can create a mongoose.js as a middleware where you initialize your connection like so:

const mongoose = require('mongoose');

module.exports = function () {
  const { MONGO_CLUSTER_URL = 'mongodb://127.0.0.1', MONGO_DATABASE_NAME } = process.env;

  // Configure mongoose to use Promises, because callbacks are passe.
  mongoose.Promise = global.Promise;
  // Connect to the Mongo DB
  return mongoose.connect(`${MONGO_CLUSTER_URL}/${MONGO_DATABASE_NAME}`, {
    useCreateIndex: true,
    useNewUrlParser: true,
    useUnifiedTopology: true,
  });
};
Enter fullscreen mode Exit fullscreen mode

and then in your server.js

const mongooseMiddleware = require('./middlewares/mongoose.js');

...

mongooseMiddleware()
  .then(() => createServer(app).listen())
  .catch((err) => {
    // an error occurred connecting to mongo!
    // log the error and exit
    console.error('Unable to connect to mongo.');
    console.error(err);
  });
Enter fullscreen mode Exit fullscreen mode

you can call any mongoose model after this without initializing the connection again

Collapse
bradtaniguchi profile image
Brad

I've used a few patterns:

  1. Create a connection to the DB before the app starts, provide the db connection to who needs it. @avalander provided an example.
  2. Create a map of database connections. This is the same as the first setup, but allows you to have multiple db connections open in a "pool" based upon users. (This was done due to having to connect to multiple databases)
  3. Use Dependency Injection using a lib, your own, or framework (hello nestjs)

I personally find the DI setup is the easiest, but requires the most legwork to get setup. The architecture scales better once you have more dependencies, or dependencies of dependencies or more complexity beyond waiting for 1 thing to "load". (you need to get a config to load before starting mongo, passing extra DB connections to multiple controllers, etc)

Generally opening 1 mongodb connection per request is the worst way to go about things, unless you only have 1 endpoint and or client-side caching or some other layer of caching.

Collapse
perigk profile image
Periklis Gkolias Author

Apart from too many articles, I have seen in actual apps too, unfortunately (the one connection per request). I will have a look at nest.js