Mongoose Setup
Mongoose is the most popular Object Document Mapper (ODM) for MongoDB in the Node.js ecosystem, and NestJS ships a first-class integration through the @nestjs/mongoose package. It wraps Mongoose’s connection lifecycle in a dedicated module, exposes schemas and models through the dependency injection container, and gives you clean, async-friendly configuration. This page covers installing the package, opening a connection with MongooseModule.forRoot, configuring connection options synchronously and asynchronously, and wiring up multiple named connections.
Installing the packages
Install the NestJS Mongoose wrapper alongside Mongoose itself. Mongoose carries its own TypeScript definitions, so no separate @types package is needed.
npm install @nestjs/mongoose mongoose
If you plan to load the connection URI from environment variables (recommended), also install the config module:
npm install @nestjs/config
Connecting with MongooseModule.forRoot
The simplest way to connect is to import MongooseModule.forRoot() once in your root AppModule. The first argument is the MongoDB connection string; the optional second argument accepts Mongoose connection options.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/devcraftly'),
],
})
export class AppModule {}
NestJS opens the connection when the application bootstraps and gracefully closes it on shutdown. When the connection succeeds you will see Mongoose-backed providers become available to the rest of your app.
Output:
[Nest] 4821 - 06/14/2026, 10:22:14 AM [InstanceLoader] MongooseModule dependencies initialized +6ms
[Nest] 4821 - 06/14/2026, 10:22:14 AM [NestApplication] Nest application successfully started +12ms
Avoid hard-coding the connection string. Production credentials belong in environment variables loaded through
@nestjs/config, never in source control.
Configuring connection options
The second argument to forRoot is a MongooseModuleOptions object that extends Mongoose’s native connect options. These tune buffering, pool size, timeouts, and the default database.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/devcraftly', {
dbName: 'devcraftly',
maxPoolSize: 20,
minPoolSize: 2,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
autoIndex: false,
}),
],
})
export class AppModule {}
The most frequently used options:
| Option | Type | Purpose |
|---|---|---|
dbName | string | Database to use, overriding any name in the URI. |
maxPoolSize | number | Maximum sockets the driver keeps open (default 100). |
minPoolSize | number | Minimum sockets kept warm in the pool. |
serverSelectionTimeoutMS | number | How long to try selecting a server before erroring. |
socketTimeoutMS | number | How long a socket stays inactive before closing. |
autoIndex | boolean | Build schema indexes on startup; disable in production. |
retryWrites | boolean | Automatically retry certain write operations. |
Set
autoIndex: falsein production. Building indexes on every boot is expensive on large collections; create indexes deliberately via a migration or one-off task instead.
Async configuration with forRootAsync
In real applications the connection string comes from configuration that isn’t available at module-definition time. Use MongooseModule.forRootAsync() to inject ConfigService (or any provider) and build the options at runtime.
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
MongooseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
uri: config.getOrThrow<string>('MONGODB_URI'),
dbName: config.get<string>('MONGODB_DB', 'devcraftly'),
maxPoolSize: 20,
serverSelectionTimeoutMS: 5000,
}),
}),
],
})
export class AppModule {}
The useFactory returns the same MongooseModuleOptions shape, with the connection string supplied as the uri property. getOrThrow fails fast at startup if the variable is missing, which is safer than silently connecting to the wrong database.
Reacting to the connection with connectionFactory
If you need to hook into the raw Mongoose Connection, for example to register global plugins or log connection events, supply a connectionFactory.
import { MongooseModule } from '@nestjs/mongoose';
import { Connection } from 'mongoose';
MongooseModule.forRootAsync({
useFactory: () => ({
uri: process.env.MONGODB_URI,
connectionFactory: (connection: Connection) => {
connection.on('connected', () => console.log('MongoDB connected'));
connection.on('disconnected', () => console.warn('MongoDB lost'));
connection.plugin(require('mongoose-autopopulate'));
return connection;
},
}),
});
Output:
MongoDB connected
Multiple named connections
A single application can talk to several MongoDB databases by giving each connection a unique connectionName. The name is then used everywhere you reference that connection, including when registering schemas with forFeature.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/users', {
connectionName: 'users',
}),
MongooseModule.forRoot('mongodb://localhost:27017/analytics', {
connectionName: 'analytics',
}),
],
})
export class AppModule {}
To inject a specific connection’s model in a feature module, pass the connection name as the second argument to forFeature, and use @InjectConnection('users') to grab the raw connection.
import { Injectable } from '@nestjs/common';
import { InjectConnection } from '@nestjs/mongoose';
import { Connection } from 'mongoose';
@Injectable()
export class HealthService {
constructor(
@InjectConnection('users') private readonly usersConn: Connection,
) {}
isHealthy(): boolean {
// readyState 1 means connected
return this.usersConn.readyState === 1;
}
}
Best Practices
- Load the connection URI from
@nestjs/configwithforRootAsyncand usegetOrThrowso a missing variable fails the boot loudly. - Disable
autoIndexin production and manage indexes explicitly to avoid slow, repeated index builds. - Tune
maxPoolSizeandserverSelectionTimeoutMSto match your workload rather than relying on driver defaults. - Give every connection an explicit
connectionNameonce you go beyond a single database, and reference that name consistently inforFeatureand@InjectConnection. - Register a
connectionFactoryto logconnected/disconnectedevents so you can observe connection health in production. - Keep the
MongooseModule.forRootimport in the root module only; register schemas per feature withMongooseModule.forFeature.