NestJS Mongoose Setup

Mongoose is the most popular library for working with MongoDB in Node.js. It adds schemas, models, and validation on top of MongoDB's flexible document structure. NestJS provides an official @nestjs/mongoose package that integrates Mongoose cleanly into the module and dependency injection system.

MongoDB vs Relational Databases

MongoDB stores data as documents (JSON-like objects) inside collections, rather than rows in tables. There are no fixed columns — each document can have different fields. This flexibility suits applications where data shape varies or evolves frequently.

Relational DB (PostgreSQL)        MongoDB
──────────────────────────────    ──────────────────────────────
Tables                            Collections
Rows                              Documents
Columns                           Fields
Schema enforced by DB             Schema enforced by Mongoose
SQL JOINs                         Embedded documents or refs

Example document in MongoDB:
{
  "_id": "abc123",
  "name": "Alice",
  "email": "alice@example.com",
  "tags": ["admin", "editor"],
  "address": {
    "city": "New York",
    "zip": "10001"
  }
}

Installing Mongoose for NestJS

npm install @nestjs/mongoose mongoose

Connecting to MongoDB

Import MongooseModule in your root module with the MongoDB connection string:

// app.module.ts
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:27017/myapp'),
  ],
})
export class AppModule {}

The connection string format: mongodb://[host]:[port]/[database-name]. For a remote MongoDB Atlas cluster, the connection string looks like mongodb+srv://user:pass@cluster.mongodb.net/myapp.

Using Environment Variables

Store your connection string in an environment variable to avoid exposing credentials in code:

// .env file
MONGODB_URI=mongodb://localhost:27017/myapp

// app.module.ts
MongooseModule.forRoot(process.env.MONGODB_URI),

Async Connection with ConfigService

When you use the NestJS ConfigModule, use the async version to delay the connection until configuration is available:

MongooseModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: (configService: ConfigService) => ({
    uri: configService.get<string>('MONGODB_URI'),
  }),
  inject: [ConfigService],
}),

Registering Schemas in Feature Modules

Each schema registers in the feature module that uses it. Unlike TypeORM entities which define database columns directly, Mongoose schemas require an explicit schema definition (covered in the next topic):

// users.module.ts
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './user.schema';

@Module({
  imports: [
    MongooseModule.forFeature([
      { name: User.name, schema: UserSchema }
    ]),
  ],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

Connection Setup Diagram

NestJS App
┌────────────────────────────────────────┐
│  AppModule                             │
│    MongooseModule.forRoot(uri)         │
│              |                         │
│              v                         │
│    Mongoose creates connection pool    │
│              |                         │
│              v                         │
│  UsersModule                           │
│    MongooseModule.forFeature([...])    │
│              |                         │
│              v                         │
│   User model injectable in UsersService│
└────────────────────────────────────────┘
          |
          v
   MongoDB Database
   ┌──────────────────┐
   │  users collection│
   │  posts collection│
   └──────────────────┘

MongoDB Atlas — Cloud Database

For production and cloud deployments, MongoDB Atlas provides a fully managed MongoDB service. Create a free cluster at cloud.mongodb.com, whitelist your server's IP address, create a database user, and copy the connection string into your environment variables. No server administration needed.

Connection Events and Debugging

When the connection is established successfully, Mongoose logs a connection event. If the connection fails (wrong URI, database unreachable, wrong credentials), your NestJS application throws an error at startup and exits. This fail-fast behavior is intentional — a server that cannot reach its database should not pretend to be running.

Enable Mongoose debug mode to log every query to the console during development:

MongooseModule.forRoot(uri, {
  connectionFactory: (connection) => {
    connection.set('debug', true);
    return connection;
  }
}),

Debug mode shows every MongoDB operation in the terminal — useful for verifying that your service methods produce the correct queries, or for spotting N+1 query problems where a loop triggers many separate database calls.

When to Choose Mongoose Over TypeORM

Choose TypeORM (SQL) when:
  - Data has clear, stable relationships
  - You need strong ACID transactions
  - Your schema is well-defined upfront

Choose Mongoose (MongoDB) when:
  - Data shape varies between records
  - You need to store nested documents naturally
  - Flexible schema or rapid prototyping
  - Your team already uses MongoDB

Both approaches integrate smoothly into NestJS through the same module registration pattern. The dependency injection system works identically for both — the only difference is what you inject: a TypeORM repository or a Mongoose model.

Leave a Comment

Your email address will not be published. Required fields are marked *