DocsHub
Mongoose

Connecting to MongoDB with Mongoose

Learn how to connect to MongoDB using mongoose.connect(), handle connection events, and structure your connection for a real application.

Connecting to MongoDB with Mongoose

Before you can use any Mongoose model, you need to connect to MongoDB. Mongoose manages the connection for you — you connect once when your app starts, and Mongoose keeps the connection alive for as long as your app is running.


Basic Connection

const mongoose = require('mongoose');

await mongoose.connect('mongodb://localhost:27017/school');
console.log('Connected to MongoDB');

That is the minimum. One line connects your app to the school database on your local MongoDB server.

Unlike the raw MongoDB driver, you do not need to pass the database name separately — it is part of the connection string. Mongoose uses it automatically for all models.


Connection String

The connection string tells Mongoose where MongoDB is running:

mongodb://username:password@host:port/database

Local MongoDB

await mongoose.connect('mongodb://localhost:27017/school');

Local MongoDB with authentication

await mongoose.connect('mongodb://admin:password123@localhost:27017/school');

MongoDB Atlas

await mongoose.connect('mongodb+srv://schooladmin:password@cluster.abc12.mongodb.net/school');

The connection string format is exactly the same as what we covered in the Basics section. Mongoose uses the same MongoDB driver underneath — it just wraps it.

Never hardcode your connection string in your code. Store it in an environment variable and load it with dotenv:

require('dotenv').config();
await mongoose.connect(process.env.MONGODB_URI);

Connection Options

You can pass an options object as the second argument to mongoose.connect():

await mongoose.connect(process.env.MONGODB_URI, {
  serverSelectionTimeoutMS: 5000,  // timeout after 5s if can't connect
  socketTimeoutMS: 45000,          // close sockets after 45s of inactivity
  maxPoolSize: 10                  // max 10 simultaneous connections
});

Most of the time the defaults work fine. These options become important in production when you need to tune connection behavior.

Common options

OptionWhat it doesDefault
serverSelectionTimeoutMSHow long to wait to find a server before throwing an error30000
socketTimeoutMSHow long to wait on a socket before timing out0 (no timeout)
maxPoolSizeMaximum number of connections in the pool5
minPoolSizeMinimum connections to keep open0
connectTimeoutMSHow long to wait for initial connection30000

Connection Events

Mongoose emits events on the connection object. Listening to these is useful for logging and debugging:

const mongoose = require('mongoose');

// Successfully connected
mongoose.connection.on('connected', () => {
  console.log('Mongoose connected to MongoDB');
});

// Connection error
mongoose.connection.on('error', (err) => {
  console.error('Mongoose connection error:', err);
});

// Disconnected
mongoose.connection.on('disconnected', () => {
  console.log('Mongoose disconnected from MongoDB');
});

// Connect
await mongoose.connect(process.env.MONGODB_URI);

Checking Connection State

Mongoose has a readyState property that tells you the current connection state:

const state = mongoose.connection.readyState;
ValueMeaning
0Disconnected
1Connected
2Connecting
3Disconnecting
if (mongoose.connection.readyState === 1) {
  console.log('Connected');
} else {
  console.log('Not connected');
}

Disconnecting

When your app shuts down, close the connection cleanly:

await mongoose.disconnect();
console.log('Disconnected from MongoDB');

For a Node.js server, listen for the process termination signal and disconnect:

process.on('SIGINT', async () => {
  await mongoose.disconnect();
  console.log('MongoDB connection closed');
  process.exit(0);
});

SIGINT is triggered when you press Ctrl+C to stop your app.


Structuring the Connection in a Real App

In a real application, you do not call mongoose.connect() directly in every file. You create a dedicated connection file and import it once when your app starts.

db.js — connection file

const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI);
    console.log(`MongoDB connected: ${mongoose.connection.host}`);
  } catch (error) {
    console.error('MongoDB connection failed:', error.message);
    process.exit(1); // exit the app if connection fails
  }
};

module.exports = connectDB;

app.js — call it once at startup

require('dotenv').config();
const express = require('express');
const connectDB = require('./db');

const app = express();

// Connect to database first
await connectDB();

// Then start the server
app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Mongoose maintains a connection pool internally — multiple connections ready to handle requests simultaneously. You only call mongoose.connect() once. After that, every model you use shares the same pool automatically. There is no need to pass connections around your app.


Multiple Connections

By default, Mongoose uses a single global connection. But if you need to connect to multiple databases, create separate connections using mongoose.createConnection():

const schoolDB = mongoose.createConnection('mongodb://localhost:27017/school');
const libraryDB = mongoose.createConnection('mongodb://localhost:27017/library');

// Create models on specific connections
const Student = schoolDB.model('Student', studentSchema);
const Book = libraryDB.model('Book', bookSchema);

For most applications — including our school system — the single global connection is all you need.


Connection in Our School System

Here is the complete connection setup for our school management system:

.env file

MONGODB_URI=mongodb://localhost:27017/school

db.js

const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGODB_URI);

    console.log(`MongoDB connected: ${conn.connection.host}`);
    console.log(`Database: ${conn.connection.name}`);

  } catch (error) {
    console.error(`Connection error: ${error.message}`);
    process.exit(1);
  }
};

// Clean disconnect on app shutdown
process.on('SIGINT', async () => {
  await mongoose.disconnect();
  console.log('MongoDB disconnected — app shutdown');
  process.exit(0);
});

module.exports = connectDB;

index.js

require('dotenv').config();
const connectDB = require('./db');

const start = async () => {
  await connectDB();

  // Import models after connection
  const Student = require('./models/Student');
  const Teacher = require('./models/Teacher');
  const Course  = require('./models/Course');

  // Your app logic here
  const students = await Student.find({ enrolled: true });
  console.log(`Found ${students.length} enrolled students`);
};

start();

Quick Reference

// Connect
await mongoose.connect(process.env.MONGODB_URI);

// Check state
mongoose.connection.readyState; // 1 = connected

// Listen for events
mongoose.connection.on('connected', () => console.log('Connected'));
mongoose.connection.on('error', (err) => console.error(err));
mongoose.connection.on('disconnected', () => console.log('Disconnected'));

// Disconnect
await mongoose.disconnect();

Always handle the connection error case and call process.exit(1) if the initial connection fails. An app running without a database connection will throw confusing errors everywhere. Failing fast with a clear message makes debugging much easier.

On this page