Creating a CRUD API using NestJS 10 and MongoDB (Mongoose)
Add to your RSS feed30 October 20236 min readTable of Contents
What is NestJS?
NestJS, a progressive Node.js framework, has gained popularity for its ability to simplify the development of server-side applications. MongoDB, a NoSQL database, is known for its flexibility and scalability. Combining NestJS with MongoDB using Mongoose, a popular MongoDB object modeling library, allows developers to build powerful CRUD (Create, Read, Update, Delete) APIs quickly and efficiently. In this tutorial, we will walk you through the process of creating a CRUD API using NestJS and MongoDB (Mongoose).
Prerequisites:
Before we begin, ensure that you have Node.js and npm (Node Package Manager) installed on your system. Additionally, you should have NestJS CLI installed globally. You can install it with the following command:
npm install -g @nestjs/cliCreating a CRUD API
Installation
- Setting Up the Project:
- Install MongoDB and Mongoose:
- Set Up MongoDB:
Ensure that you have MongoDB installed and running locally. If not, you can download it from the official MongoDB website
https://www.mongodb.com/try/download/community and follow the installation instructions.
- Create a MongoDB Database:
Create a MongoDB database for our application. You can do this through the MongoDB shell or a graphical tool like MongoDB Compass.
- Connect the main NestJS module with Mongo:
1 import { Module } from '@nestjs/common';2 import { MongooseModule } from '@nestjs/mongoose';34 import { AppController } from './app.controller';5 import { AppService } from './app.service';6 import { MessagesModule } from './messages/messages.module';78 @Module({9 imports: [MongooseModule.forRoot('mongodb://localhost/nest-mongodb-crud'), MessagesModule],10 controllers: [AppController],11 providers: [AppService],12 })13 export class AppModule {}
If you have a MongoServerSelectionError connect ECONNREFUSED error - check this solution.
- Create a new Module using NestJS CLI:
Adding Routing Logic:
- Create a new Controller:
1 import { Get, Post, Delete, Put } from '@nestjs/common';2 import { Controller } from '@nestjs/common';34 import { CreateMessageDto } from './dto/create-message.dto';5 import { MessagesService } from './messages.service';67 @Controller('messages')8 export class MessagesController {9 constructor(private readonly messagesService: MessagesService) {}10 @Get()11 async allMessages() {}1213 @Post()14 async createMessage() {}1516 @Get(':id')17 async getMessage() {}1819 @Delete(':id')20 async deleteMessage() {}2122 @Put(':id')23 async updateMessage() {}24 }
- Install class-validator and class-transformer libraries
With class-validator and class-transformer, you can easily handle data validation and transformation in your NestJS application, making your API more robust and secure.
- Add auto-validation in main.ts
1 import { NestFactory } from '@nestjs/core';2 import { ValidationPipe } from '@nestjs/common';34 import { AppModule } from './app.module';56 async function bootstrap() {7 const app = await NestFactory.create(AppModule);8 app.useGlobalPipes(new ValidationPipe());9 await app.listen(3000);10 }11 bootstrap();
- Create a DTO (Data Transfer Object):
DTOs are used to define the structure of the data you expect to receive or send in your API. They are also where you define validation rules.
For example, let's create a CreateMessageDto for a Message object:
-
Create dto folder inside a messages module.
-
Create file create-message.dto.ts
1 export class CreateMessageDto {2 message: string;3 }
- Add a validation to DTO:
1 import { IsString } from 'class-validator';23 export class CreateMessageDto {4 @IsString()5 message: string;6 }
- Use DTO in Controller:
In your controller, import the DTO and use it to validate incoming data. You can use the ValidationPipe provided by NestJS to automatically validate the request body against your DTO.
1 import { Get, Param, Post, Body, Delete } from '@nestjs/common';2 import { Controller } from '@nestjs/common';34 import { CreateMessageDto } from './dto/create-message.dto';56 @Controller('messages')7 export class MessagesController {8 @Get()9 async allMessages() {}1011 @Post()12 async createMessage(@Body() dto: CreateMessageDto) {13 console.log('dto:', dto);14 }1516 @Get(':id')17 async getMessage(@Param('id') id: string) {}1819 @Delete(':id')20 async deleteMessage(@Param('id') id: string) {}2122 @Put(':id')23 async updateMessage(@Param('id') id: string, @Body() dto: CreateMessageDto) {}24 }
Now let's check it with Postman:
In VsCode you will see your message in console
- Create a new Model:
Create folder models and add the messages.model.ts file inside:
1 import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';2 import { Document } from 'mongoose';34 export type MessageDocument = Message & Document;56 @Schema()7 export class Message {8 @Prop({ required: true })9 message: string;10 }11 export const MessageSchema = SchemaFactory.createForClass(Message);
- Connect mongodb in messages model:
1 import { Module } from '@nestjs/common';2 import { MongooseModule } from '@nestjs/mongoose';34 import { MessagesController } from './messages.controller';5 import { MessagesService } from './messages.service';6 import { Message, MessageSchema } from './models/messages.model';78 @Module({9 controllers: [MessagesController],10 providers: [MessagesService],11 imports: [MongooseModule.forFeature([{ name: Message.name, schema: MessageSchema }])],12 })13 export class MessagesModule {}
- Create a new Service:
1 import { Model } from 'mongoose';2 import { InjectModel } from '@nestjs/mongoose';3 import { Injectable } from '@nestjs/common';45 import { CreateMessageDto } from './dto/create-message.dto';6 import { Message } from './models/messages.model';78 @Injectable()9 export class MessagesService {10 async getAll(): Promise<Message[]> {}11 async getById(id: string): Promise<Message | null> {}12 async add(message: CreateMessageDto): Promise<Message | null> {13 return await new this.messageModel(message).save();14 }15 async delete(id: string): Promise<Message | null> {}16 async update(id: string, dto): Promise<Message | null> {}17 }
Inject the mongodb Model in NestJS Module
1 constructor(@InjectModel(Message.name) private readonly messageModel: Model<Message>) {}
Finally, fill the methods inside a service:
1 import { Model } from 'mongoose';2 import { InjectModel } from '@nestjs/mongoose';3 import { Injectable } from '@nestjs/common';45 import { CreateMessageDto } from './dto/create-message.dto';6 import { Message } from './models/messages.model';78 @Injectable()9 export class MessagesService {10 constructor(@InjectModel(Message.name) private readonly messageModel: Model<Message>) {}11 async getAll(): Promise<Message[]> {12 return await this.messageModel.find().exec();13 }14 async getById(id: string): Promise<Message | null> {15 return await this.messageModel.findById(id).exec();16 }17 async add(message: CreateMessageDto): Promise<Message | null> {18 return await new this.messageModel(message).save();19 }20 async delete(id: string) {21 return await this.messageModel.findByIdAndDelete(id).exec();22 }23 async update(id: string, dto): Promise<Message | null> {24 return await this.messageModel.findByIdAndUpdate(id, dto).exec();25 }26 }
- Inject a service inside the controller
1 import { Get, Param, Post, Body, Delete } from '@nestjs/common';2 import { Controller } from '@nestjs/common';34 import { CreateMessageDto } from './dto/create-message.dto';5 import { MessagesService } from './messages.service';67 @Controller('messages')8 export class MessagesController {9 constructor(private readonly messagesService: MessagesService) {}10 @Get()11 async allMessages() {12 return this.messagesService.getAll();13 }1415 @Post()16 async createMessage(@Body() dto: CreateMessageDto) {17 return this.messagesService.add(dto);18 }1920 @Get(':id')21 async getMessage(@Param('id') id: string) {22 return this.messagesService.getById(id);23 }2425 @Delete(':id')26 async deleteMessage(@Param('id') id: string) {27 return this.messagesService.delete(id);28 }2930 @Put(':id')31 async updateMessage(@Param('id') id: string, @Body() dto: CreateMessageDto) {32 return this.messagesService.update(id, dto);33 }34 }
Testing the API:
Start your NestJS application by running:
npm run startYou can now test your CRUD API using tools like Postman or by making HTTP requests from your frontend application.
Here are some sample HTTP requests:
-
Create a Message: POST
http://localhost:3000/messages
-
Get All Messages: GET
http://localhost:3000/messages
-
Get a Message by ID: GET
http://localhost:3000/messages/:id
-
Update a Message: PUT
http://localhost:3000/messages/:id
-
Delete a Message: DELETE
http://localhost:3000/messages/:id
Conclusion:
In this tutorial, we've explored how to create a CRUD API using NestJS and MongoDB with Mongoose. You learned how to set up a NestJS project, define a model, create a service and controller, connect to a MongoDB database, and test your API. With this foundation, you can expand and customize your API to suit your specific application requirements and build robust backend services efficiently. NestJS, combined with MongoDB and Mongoose, provides a powerful stack for building scalable and performant applications. Happy coding!