How to Connect to MongoDB from Node.js

6 min readMongoDB

MongoDB is the database Node developers reach for most often, and the connection story is correspondingly mature. There are two real choices: the official mongodb driver, and Mongoose, an ODM (object-document mapper) layered on top of it. This guide shows working code for both, the configuration that matters (pooling, SRV strings, TLS), and the connection errors you'll hit first.

The options

LibraryPackageWhat it isVersion (as of June 2026)
Official drivermongodbThe low-level driver. Direct access to collections, raw documents.7.3.0
MongoosemongooseODM with schemas, validation, middleware, populate. Bundles the driver.9.7.0

Rules of thumb:

  • You want schemas, validation, and model-level structure (most application backends): Mongoose. It enforces shape on a schemaless database, which is usually what you actually want.
  • You want raw driver access, minimum abstraction, or you're building tooling/scripts: the official mongodb driver. Mongoose installs it anyway, so there's no extra dependency cost to dropping down to it.

Mongoose is not a replacement for the driver, it's a layer on top. Under load, both pool connections the same way because Mongoose delegates to the driver's pool.

The official driver

npm install mongodb
import { MongoClient } from "mongodb";
 
const uri = "mongodb://127.0.0.1:27017";
const client = new MongoClient(uri);
 
try {
  await client.connect();
  const db = client.db("myapp");
  const users = db.collection("users");
 
  const user = await users.findOne({ email: "ada@example.com" });
  console.log(user);
} finally {
  await client.close();
}

One client for the whole app

MongoClient is not a single socket. It owns a connection pool and is designed to be created once and shared for the lifetime of the process. The common mistake is opening a new client per request, which exhausts connections and tanks throughput. Create it at startup, reuse the instance everywhere, close it on shutdown.

// db.js -- a single shared client
import { MongoClient } from "mongodb";
 
const client = new MongoClient(process.env.MONGODB_URI, {
  maxPoolSize: 20,    // default is 100
  minPoolSize: 0,
});
 
let connected;
export async function getDb() {
  if (!connected) connected = client.connect();
  await connected;
  return client.db("myapp");
}

client.connect() is also idempotent in modern versions and largely optional -- the driver connects lazily on first operation. Calling it explicitly at startup is still worth it: you find out about a bad URI or unreachable host immediately instead of on the first query.

Mongoose

npm install mongoose
import mongoose from "mongoose";
 
await mongoose.connect("mongodb://127.0.0.1:27017/myapp");
 
const User = mongoose.model("User", new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  name: String,
  createdAt: { type: Date, default: Date.now },
}));
 
const user = await User.findOne({ email: "ada@example.com" });
console.log(user);

mongoose.connect() uses one shared connection (the default connection) for all models. Like the driver's client, call it once at startup, not per request. Mongoose buffers model operations issued before the connection is ready, so you won't get errors if a query fires during the connection window -- it queues until the socket is up.

Connection strings

Two forms:

# Standard -- explicit host(s) and port
mongodb://user:pass@host1:27017,host2:27017/myapp?replicaSet=rs0

# SRV -- one DNS record resolves to the hosts. Atlas uses this.
mongodb+srv://user:pass@cluster0.abcde.mongodb.net/myapp

mongodb+srv:// looks up the cluster's members via a DNS SRV record, so you don't hardcode hostnames. It implies tls=true automatically. It's the format MongoDB Atlas hands you, and you don't need an extra DNS package -- SRV resolution is built into the driver.

Percent-encode credentials. If your username or password contains @, :, /, or other reserved characters, encode them or the URI parser will misread the string. p@ss becomes p%40ss.

Three traps that catch everyone

localhost resolves to IPv6. Node 18+ prefers IPv6, so on many machines localhost resolves to ::1, but a default MongoDB install only listens on the IPv4 127.0.0.1. The result is a confusing ECONNREFUSED against a server that's clearly running. Use 127.0.0.1 in your connection string for local databases, or bind MongoDB to ::1 as well.

authSource. Your user is created in one database (often admin) but you're connecting to another (myapp). The driver authenticates against the database in the path by default, so you get Authentication failed. Add ?authSource=admin (or wherever the user lives). On Atlas this is handled for you; on self-hosted it bites constantly.

Atlas IP allowlist. A first connection to Atlas that times out with a server-selection error is almost always your current IP not being on the cluster's allowlist, not a code problem. Check Network Access in the Atlas console before debugging your URI.

Pooling, the short version

The driver opens sockets on demand up to maxPoolSize (default 100) and reuses them. You rarely need to tune this for a single process. Where it matters:

  • Serverless (Lambda, Vercel functions): cache the client across invocations on a module-level/global variable so warm instances reuse the pool instead of opening a new one per request. A cold start opens one pool; a leaked client-per-invocation opens hundreds.
  • Many process replicas: total connections to the server is roughly maxPoolSize × number of processes. Size it against the server's connection limit (Atlas tiers cap this).

Graceful shutdown

Close the client (or mongoose.disconnect()) when the process is terminating so in-flight operations finish and sockets close cleanly:

process.on("SIGINT", async () => {
  await client.close();
  process.exit(0);
});

Error reference

ErrorCauseFix
ECONNREFUSED ::1:27017localhost resolved to IPv6, server only on IPv4Use 127.0.0.1
Authentication failedWrong authSource, or bad credentialsAdd ?authSource=admin; check user/password
MongoServerSelectionErrorCan't reach any cluster member (timeout)Atlas IP allowlist, network path, wrong host
URI must include hostname...Malformed connection stringCheck format; percent-encode credentials
MongoParseErrorUnencoded special char in user/passPercent-encode (@%40)

Connecting with a GUI instead

For exploring collections, checking document shapes, or sketching the aggregation pipeline you're about to embed in code, a client beats console.log. Mako connects to MongoDB with AI-assisted query building; the same guide compares Compass, Studio 3T, and the rest of the field honestly.

For the query side once you're connected, see our MongoDB guides on the aggregation pipeline, query operators, and indexes. For loading data, see importing CSV to MongoDB.


Mako connects to MongoDB (and 8 other databases) with AI-powered autocomplete. Try it free at mako.ai.

Mako — open source

Skip the terminal. Use Mako.

Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.