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/databaseLocal 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
| Option | What it does | Default |
|---|---|---|
serverSelectionTimeoutMS | How long to wait to find a server before throwing an error | 30000 |
socketTimeoutMS | How long to wait on a socket before timing out | 0 (no timeout) |
maxPoolSize | Maximum number of connections in the pool | 5 |
minPoolSize | Minimum connections to keep open | 0 |
connectTimeoutMS | How long to wait for initial connection | 30000 |
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;| Value | Meaning |
|---|---|
0 | Disconnected |
1 | Connected |
2 | Connecting |
3 | Disconnecting |
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/schooldb.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.