JavaScript Development Space

Creating a CRUD API using NestJS 10 and MongoDB (Mongoose)

Add to your RSS feed30 October 20236 min read
Creating a CRUD API using NestJS 10 and MongoDB (Mongoose)

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/cli

Creating a CRUD API

Installation

  1. Setting Up the Project:
nest new nest-mongodb-crud
  1. Install MongoDB and Mongoose:
npm install @nestjs/mongoose mongoose
  1. 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.

  1. 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.

  1. Connect the main NestJS module with Mongo:
js
1 import { Module } from '@nestjs/common';
2 import { MongooseModule } from '@nestjs/mongoose';
3
4 import { AppController } from './app.controller';
5 import { AppService } from './app.service';
6 import { MessagesModule } from './messages/messages.module';
7
8 @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.

  1. Create a new Module using NestJS CLI:
nest generate module messages

Adding Routing Logic:

  1. Create a new Controller:
nest generate controller messages/messages --flat
js
1 import { Get, Post, Delete, Put } from '@nestjs/common';
2 import { Controller } from '@nestjs/common';
3
4 import { CreateMessageDto } from './dto/create-message.dto';
5 import { MessagesService } from './messages.service';
6
7 @Controller('messages')
8 export class MessagesController {
9 constructor(private readonly messagesService: MessagesService) {}
10 @Get()
11 async allMessages() {}
12
13 @Post()
14 async createMessage() {}
15
16 @Get(':id')
17 async getMessage() {}
18
19 @Delete(':id')
20 async deleteMessage() {}
21
22 @Put(':id')
23 async updateMessage() {}
24 }
  1. Install class-validator and class-transformer libraries
npm install class-validator class-transformer

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.

  1. Add auto-validation in main.ts
js
1 import { NestFactory } from '@nestjs/core';
2 import { ValidationPipe } from '@nestjs/common';
3
4 import { AppModule } from './app.module';
5
6 async function bootstrap() {
7 const app = await NestFactory.create(AppModule);
8 app.useGlobalPipes(new ValidationPipe());
9 await app.listen(3000);
10 }
11 bootstrap();
  1. 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:

  1. Create dto folder inside a messages module.

  2. Create file create-message.dto.ts

js
1 export class CreateMessageDto {
2 message: string;
3 }
  1. Add a validation to DTO:
js
1 import { IsString } from 'class-validator';
2
3 export class CreateMessageDto {
4 @IsString()
5 message: string;
6 }
  1. 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.

js
1 import { Get, Param, Post, Body, Delete } from '@nestjs/common';
2 import { Controller } from '@nestjs/common';
3
4 import { CreateMessageDto } from './dto/create-message.dto';
5
6 @Controller('messages')
7 export class MessagesController {
8 @Get()
9 async allMessages() {}
10
11 @Post()
12 async createMessage(@Body() dto: CreateMessageDto) {
13 console.log('dto:', dto);
14 }
15
16 @Get(':id')
17 async getMessage(@Param('id') id: string) {}
18
19 @Delete(':id')
20 async deleteMessage(@Param('id') id: string) {}
21
22 @Put(':id')
23 async updateMessage(@Param('id') id: string, @Body() dto: CreateMessageDto) {}
24 }

Now let's check it with Postman:

Postman Postman

In VsCode you will see your message in console

VsCode VsCode

  1. Create a new Model:

Create folder models and add the messages.model.ts file inside:

js
1 import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
2 import { Document } from 'mongoose';
3
4 export type MessageDocument = Message & Document;
5
6 @Schema()
7 export class Message {
8 @Prop({ required: true })
9 message: string;
10 }
11 export const MessageSchema = SchemaFactory.createForClass(Message);
  1. Connect mongodb in messages model:
js
1 import { Module } from '@nestjs/common';
2 import { MongooseModule } from '@nestjs/mongoose';
3
4 import { MessagesController } from './messages.controller';
5 import { MessagesService } from './messages.service';
6 import { Message, MessageSchema } from './models/messages.model';
7
8 @Module({
9 controllers: [MessagesController],
10 providers: [MessagesService],
11 imports: [MongooseModule.forFeature([{ name: Message.name, schema: MessageSchema }])],
12 })
13 export class MessagesModule {}
  1. Create a new Service:
nest generate service messages --flat
js
1 import { Model } from 'mongoose';
2 import { InjectModel } from '@nestjs/mongoose';
3 import { Injectable } from '@nestjs/common';
4
5 import { CreateMessageDto } from './dto/create-message.dto';
6 import { Message } from './models/messages.model';
7
8 @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

js
1 constructor(@InjectModel(Message.name) private readonly messageModel: Model<Message>) {}

Finally, fill the methods inside a service:

js
1 import { Model } from 'mongoose';
2 import { InjectModel } from '@nestjs/mongoose';
3 import { Injectable } from '@nestjs/common';
4
5 import { CreateMessageDto } from './dto/create-message.dto';
6 import { Message } from './models/messages.model';
7
8 @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 }
  1. Inject a service inside the controller
js
1 import { Get, Param, Post, Body, Delete } from '@nestjs/common';
2 import { Controller } from '@nestjs/common';
3
4 import { CreateMessageDto } from './dto/create-message.dto';
5 import { MessagesService } from './messages.service';
6
7 @Controller('messages')
8 export class MessagesController {
9 constructor(private readonly messagesService: MessagesService) {}
10 @Get()
11 async allMessages() {
12 return this.messagesService.getAll();
13 }
14
15 @Post()
16 async createMessage(@Body() dto: CreateMessageDto) {
17 return this.messagesService.add(dto);
18 }
19
20 @Get(':id')
21 async getMessage(@Param('id') id: string) {
22 return this.messagesService.getById(id);
23 }
24
25 @Delete(':id')
26 async deleteMessage(@Param('id') id: string) {
27 return this.messagesService.delete(id);
28 }
29
30 @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 start

You 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:

  1. Create a Message: POST

    http://localhost:3000/messages

  2. Get All Messages: GET http://localhost:3000/messages

  3. Get a Message by ID: GET

    http://localhost:3000/messages/:id

  4. Update a Message: PUT

    http://localhost:3000/messages/:id

  5. Delete a Message: DELETE http://localhost:3000/messages/:id

Github repository

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!

Related Posts:

JavaScript Development Space

© 2024 JavaScript Development Space - Master JS and NodeJS. All rights reserved.