Applications and events

Connect from Node.js

Create one shared MongoClient, keep credentials on the server, select collections explicitly, and close resources in scripts and tests.

9 minute lesson

~~~

Install the official driver and put the client in one infrastructure module:

npm install mongodb
// db.js
import { MongoClient } from 'mongodb'

const client = new MongoClient(process.env.MONGODB_URI)

export async function getDb() {
  await client.connect()
  return client.db('animals')
}

export async function closeDb() {
  await client.close()
}

Two decisions are baked in here. The connection string lives in an environment variable, so credentials never land in the repository — and never, ever in browser JavaScript. And there is exactly one MongoClient for the whole process. The client manages a connection pool internally; creating a new client per request creates a new pool per request, and under load you exhaust the server’s connections. Calling connect() twice on the same client is safe, it returns the existing connection.

A long-running API server connects once and reuses the client forever. Short scripts and tests are different: they must close the client, or the open pool keeps the Node.js event loop alive and the process never exits. A script that “hangs at the end” almost always forgot this. Close in a finally block so it happens on failure too:

import { getDb, closeDb } from './db.js'

try {
  const animals = (await getDb()).collection('animals')

  await animals.insertOne({ name: 'Roger', species: 'dog' })
  const roger = await animals.findOne({ name: 'Roger' })
  console.log(roger)
  // { _id: new ObjectId('688f4a2e...'), name: 'Roger', species: 'dog' }
} finally {
  await closeDb()
}

Run it and verify both things: the document prints, and the process exits on its own. That second check is the point of the exercise.

Pass collections in, not the client around

Business logic that imports the database module directly is painful to test. Pass the collection as an argument instead:

export async function registerVisit(animals, name) {
  return animals.updateOne({ name }, { $inc: { visits: 1 } })
}

Tests can now call registerVisit() with a collection from an isolated test database and assert on real driver behavior. The function does not know or care which database it talks to — that decision stays in one place, at the edge of the application.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →