Using Mongoose with NestJS for MongoDB: A Comprehensive Guide

@rnab
3 min readFeb 1, 2025

--

NestJS has rapidly gained popularity as a progressive Node.js framework for building efficient and scalable server-side applications. When it comes to working with MongoDB databases within a NestJS application, Mongoose is the most recommended and widely used object data modeling (ODM) library. In this guide, we’ll delve into the details of integrating Mongoose with NestJS to interact with MongoDB, using TypeScript for enhanced productivity and type safety.

Why Mongoose?

Before diving into the integration process, let’s understand why Mongoose is a great choice for working with MongoDB:

  1. Schema-Based Modeling: Mongoose provides a strict schema-based data modeling approach which helps in structuring application data.
  2. Validation and Hooks: It offers out-of-the-box validation and middleware hooks which are useful for data preprocessing and validation logic.
  3. Rich Querying: Mongoose enables complex and powerful querying on MongoDB documents.
  4. Easy Integration: Its seamless integration with NestJS enhances developer productivity and application consistency.

Setting Up Your NestJS Application

To start, we’ll create a new NestJS application and install the necessary packages.

Step 1: Create a New NestJS Project

First, we will create a new NestJS project using the Nest CLI:

npm i -g @nestjs/cli
nest new nest-mongoose-tutorial
cd nest-mongoose-tutorial

This command scaffolds a new NestJS project with the default settings.

Step 2: Install Mongoose

Next, install Mongoose and the NestJS Mongoose package:

npm install mongoose @nestjs/mongoose

Configuring Mongoose in NestJS

To configure Mongoose, we’ll connect it to a MongoDB database by modifying the app.module.ts file. We'll also create a .env file to store our MongoDB URI.

Step 3: Connect Mongoose to MongoDB

In app.module.ts, configure Mongoose like this:

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { CatsModule } from './cats/cats.module'; // Import your module here

@Module({
imports: [
MongooseModule.forRoot(process.env.MONGODB_URI),
CatsModule,
],
})
export class AppModule {}

Create a .env file in your project root:

MONGODB_URI=mongodb://localhost/nest

Step 4: Create a Mongoose Schema

Let’s create a simple schema for a Cat entity. We'll define its schema in the cats.schema.ts file.

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

export type CatDocument = Cat & Document;

@Schema()
export class Cat {
@Prop({ required: true })
name: string;

@Prop()
age: number;

@Prop()
breed: string;
}

export const CatSchema = SchemaFactory.createForClass(Cat);

Step 5: Register the Schema in a Module

Ensure the schema is available in the module where you’ll use it. For example, in cats.module.ts:

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { CatsService } from './cats.service';
import { CatsController } from './cats.controller';
import { Cat, CatSchema } from './schemas/cat.schema';

@Module({
imports: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])],
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}

Step 6: Implement CRUD Operations

Create a cats.service.ts file to handle database operations using Mongoose models. Here’s a basic implementation of CRUD operations:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Cat, CatDocument } from './schemas/cat.schema';
import { CreateCatDto } from './dto/create-cat.dto';

@Injectable()
export class CatsService {
constructor(@InjectModel(Cat.name) private catModel: Model<CatDocument>) {}

async create(createCatDto: CreateCatDto): Promise<Cat> {
const createdCat = new this.catModel(createCatDto);
return createdCat.save();
}

async findAll(): Promise<Cat[]> {
return this.catModel.find().exec();
}

async findOne(id: string): Promise<Cat> {
return this.catModel.findById(id).exec();
}

async update(id: string, updateCatDto: any): Promise<Cat> {
return this.catModel.findByIdAndUpdate(id, updateCatDto, { new: true }).exec();
}

async remove(id: string): Promise<Cat> {
return this.catModel.findByIdAndRemove(id).exec();
}
}

Here is what our CreateCatDto might look like in create-cat.dto.ts:

export class CreateCatDto {
readonly name: string;
readonly age: number;
readonly breed: string;
}

Step 7: Create a Controller

Next, implement a basic controller in cats.controller.ts to link HTTP requests to our service methods:

import { Controller, Get, Post, Body, Param, Delete, Put } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';

@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}

@Post()
async create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}

@Get()
async findAll() {
return this.catsService.findAll();
}

@Get(':id')
async findOne(@Param('id') id: string) {
return this.catsService.findOne(id);
}

@Put(':id')
async update(@Param('id') id: string, @Body() updateCatDto: any) {
return this.catsService.update(id, updateCatDto);
}

@Delete(':id')
async remove(@Param('id') id: string) {
return this.catsService.remove(id);
}
}

Conclusion

With this setup, you now have a fully functional CRUD API powered by NestJS, Mongoose, and MongoDB. This guide covered the essentials to get you started with these technologies in a type-safe, scalable manner using TypeScript.

From here, you can expand your application by adding more features, enhancing error handling, and implementing robust validation to ensure your data integrity. Happy coding!

--

--

@rnab
@rnab

Written by @rnab

Typescript, Devops, Kubernetes, AWS, AI/ML, Algo Trading

No responses yet