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:
- Schema-Based Modeling: Mongoose provides a strict schema-based data modeling approach which helps in structuring application data.
- Validation and Hooks: It offers out-of-the-box validation and middleware hooks which are useful for data preprocessing and validation logic.
- Rich Querying: Mongoose enables complex and powerful querying on MongoDB documents.
- 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!