Welcome folks today in this blog post we will be building a nestjs crud api in mongodb database using mongoose library in typescript. All the full source code of the application is shown below.
Get Started
In order to get started you need to install the nest.js cli globally inside your system. For this you need to execute the below command on the terminal as shown below
npm i -g @nestjs/cli
Now after installing nest.js cli you need to create new nest.js project by following the given commands as shown below
nest new my-nest-project
Here in this command my-nest-project is the name of your project. Just replace here your project name.
cd my-nest-project
After that when project is created you need to go to the project directory by the above command
Start the nest.js app
Now to start the nest.js project on the browser you need to execute the below command
npm run start:dev
This will start the nest.js app at a development server inside your localhost at port 3000. If you open localhost:3000 inside your browser you will see the hello world message as shown below
Now as you can see we have the basic nest.js project running inside the browser. Now we need to install the libraries for this project.
First of all we will installing mongoose package for interacting with the Mongodb database as shown below
npm i @nestjs/mongoose mongoose
Adding MongoDB Connection in NestJS
Now we will be adding the mongodb connection using mongoose library in app.module.ts
file of nest.js project as shown below
Copy paste the below code inside this file
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [ MongooseModule.forRoot('mongodb://localhost:27017',{dbName: 'studentdb'}) ], controllers: [AppController], providers: [AppService], }) export class AppModule {} |
Here as you can see we are importing the MongooseModule
from @nestjs/mongoose package and also we are adding it inside the imports array. Here we are taking the example if the database is present inside your local machine.
If you are using mongodb Atlas or any url based mongodb database then the file will look something as shown below
1 2 3 4 5 |
@Module({ imports: [MongooseModule.forRoot('mongodb://<username>:<password>@localhost:27017',{dbName: 'studentdb'}), ], }) export class AppModule {} |
Basically here you need to paste the mongodb connection uri. It contains the username and password and also your dbname.
Creating the Schema in MongoDB
For creating the schema you need to make a new folder inside the src directory of your nest.js project and create a file called student.schema.ts file as shown below
student.schema.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose" @Schema() export class Student { @Prop() name: string; @Prop() roleNumber: number; @Prop() class: number; @Prop() gender: string; @Prop() marks: number; } export const StudentSchema = SchemaFactory.createForClass(Student); |
As you can inside this schema file we are using methods of mongoose first of all Prop,Schema and SchemaFactory. We are defining the schema of a single student which has various properties using prop keyword such as name which is of type string and then we have roll number which is of type number and then we have gender which is again of type string and then we have marks column which is of type number. And lastly we are exporting this schema by using SchemaFactory.createForClass() Method passing the schema defined.
And now we need to register this schema inside the app.module.ts
which is the root file for our nest.js project. Just include this file as shown below
app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { StudentSchema } from './schema/student.schema'; @Module({ imports: [MongooseModule.forRoot('mongodb://localhost:27017/studentdb'), MongooseModule.forFeature([{ name: 'Student', schema: StudentSchema }])], controllers: [AppController], providers: [AppService], }) export class AppModule {} |
Here in this block of code as you can see we are importing the schema that we defined in the earlier step in the top of the file and then we are adding this again inside the imports array after the Database. Here we are using the forFeatures() Method of MongooseModule to attach the schema and also we are giving the collection or table name which is in this case is Student.
Creating the Student Interface
Now we need to create the interface of student model in MongoDB. Now as the model or schema is created. Now we need to create a new folder interface and inside it you need to create a student.interface.ts
file and copy paste the below code
interface/student.interface.ts
1 2 3 4 5 6 7 8 |
import { Document } from 'mongoose'; export interface IStudent extends Document{ readonly name: string; readonly roleNumber: number; readonly class: number; readonly gender: string; readonly marks: number; } |
As you can see inside this interface all the fields are readonly and we can’t modify these fields. Here we are just telling how the fields will look like and which information will be stored inside the application
Creating the DTO Files
Now we will be actually be creating the Data Transfer Objects Files. Before creating them you need to install the below libraries or packages as shown below
npm i class-validator
npm i class-transformer
npm i @nestjs/mapped-types
Now just create a dto folder inside the src folder of your nestjs project. And inside it create two files which are create-student.dto.ts and update-student.dto.ts
create-student.dto.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import { IsNotEmpty, IsNumber, IsString, MaxLength } from "class-validator"; export class CreateStudentDto { @IsString() @MaxLength(30) @IsNotEmpty() readonly name: string; @IsNumber() @IsNotEmpty() readonly roleNumber: number; @IsNumber() @IsNotEmpty() readonly class: number; @IsString() @MaxLength(30) @IsNotEmpty() readonly gender: string; @IsNumber() @IsNotEmpty() readonly marks: number; } |
Here as you can see we are importing the different validations that are put on the different columns such as maxlength and not empty validator. isNumber checks whether the field is number or not. So all these validations are defined when we are creating a brand new student inside the mongodb table.
And similarly we need to extends the same code of create Student inside update-student.dto.ts file as shown below
update-student.dto.ts
1 2 3 |
import { PartialType } from '@nestjs/mapped-types'; import { CreateStudentDto } from './create-student.dto'; export class UpdateStudentDto extends PartialType(CreateStudentDto) {} |
For these above validations to work inside the nest.js app you need to globally register these validations inside the main.ts file of your project as shown below.
To register this validations inside this file you need to copy paste the below code and modify the file as shown below
main.ts
1 2 3 4 5 6 7 8 9 10 |
import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); await app.listen(3000); } bootstrap(); |
As you can see we are using the ValidationPipe() method to register these validations. This validationPipe is imported from nest.js common module.
Creating the Service
Now we will be creating the service which will be the interface between the request made to the nest.js application and the database. In this service we will define all the four CRUD Operations required for this application. For doing these operations we will be using the mongoose library a lot.
Just execute the below command to create a nest.js service in the command line as shown below
nest generate service student
As you can see in the below screenshot it will create the student folder and inside it we will have two files
Now inside the student.service.ts file we will now the code for crud operations. So let’s start writing the service
student.service.ts
1 2 3 4 5 6 7 8 9 10 11 |
import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { IStudent } from 'src/interface/student.interface'; @Injectable() export class StudentService { constructor(@InjectModel('Student') private studentModel:Model<IStudent>) { } } |
First of all we will have a simple constructor here inside this service. In this constructor we are importing the actual Model, Interface and also injectable from nestjs. Now we need to define the operations
- Create Student
1 2 3 4 |
async createStudent(createStudentDto: CreateStudentDto): Promise<IStudent> { const newStudent = await new this.studentModel(createStudentDto); return newStudent.save(); } |
Now this will be the first operation where we have an async function in this we are inserting the student inside the database using the Student Model in argument we are passing the dto object of the createStudent. And then using the mongoose save() method to save the data inside the database.
2) Read All Students
1 2 3 4 5 6 7 |
async getAllStudents(): Promise<IStudent[]> { const studentData = await this.studentModel.find(); if (!studentData || studentData.length == 0) { throw new NotFoundException('Students data not found!'); } return studentData; } |
Now this will be the second operation where we have an async function in this we are reading all the students list and retuning from this function.Here we are finding all the students using the find() method of mongoose.
3) Get a Specific Student By ID
1 2 3 4 5 6 7 |
async getStudent(studentId: string): Promise<IStudent> { const existingStudent = await this.studentModel.findById(studentId).exec(); if (!existingStudent) { throw new NotFoundException(`Student #${studentId} not found`); } return existingStudent; } |
As you can see in the above code we are providing the id of the student in the argument and then we are using the mongoose findById() method to find a specific student and then we are using exec() method to execute the query. And then we are returning that specific student object from the function
4) Delete the Student
1 2 3 4 5 6 7 |
async deleteStudent(studentId: string): Promise<IStudent> { const deletedStudent = await this.studentModel.findByIdAndDelete(studentId); if (!deletedStudent) { throw new NotFoundException(`Student #${studentId} not found`); } return deletedStudent; } |
As you can see in the above code we are passing the id of the student as an argument for which we need to perform the delete operation. And now we are using findByIdAndDelete() method of mongoose to select the student by it’s id and deleting that student. And then we are returning the deleted student object from the function.
5) Update the Student
1 2 3 4 5 6 7 |
async updateStudent(studentId: string, updateStudentDto: UpdateStudentDto): Promise<IStudent> { const existingStudent = await this.studentModel.findByIdAndUpdate(studentId, updateStudentDto, { new: true }); if (!existingStudent) { throw new NotFoundException(`Student #${studentId} not found`); } return existingStudent; } |
Now we will be performing the update operation on the student model. In the above function as you can see we are receiving the id of the student for which we need to perform the update operation and also we are receiving the updateStudentDto file as an argument. And now guys we are using the findByIdAndUpdate() method of mongoose to update the existing student. And lastly we are returning the updated Student Object.
Full student.service.ts Code
Wrapping all just see all the source code of the service file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { CreateStudentDto } from 'src/dto/create-student.dto'; import { UpdateStudentDto } from 'src/dto/update-student.dto'; import { IStudent } from 'src/interface/student.interface'; @Injectable() export class StudentService { constructor(@InjectModel('Student') private studentModel:Model<IStudent>) { } async createStudent(createStudentDto: CreateStudentDto): Promise<IStudent> { const newStudent = await new this.studentModel(createStudentDto); return newStudent.save(); } async getAllStudents(): Promise<IStudent[]> { const studentData = await this.studentModel.find(); if (!studentData || studentData.length == 0) { throw new NotFoundException('Students data not found!'); } return studentData; } async getStudent(studentId: string): Promise<IStudent> { const existingStudent = await this.studentModel.findById(studentId).exec(); if (!existingStudent) { throw new NotFoundException(`Student #${studentId} not found`); } return existingStudent; } async deleteStudent(studentId: string): Promise<IStudent> { const deletedStudent = await this.studentModel.findByIdAndDelete(studentId); if (!deletedStudent) { throw new NotFoundException(`Student #${studentId} not found`); } return deletedStudent; } async updateStudent(studentId: string, updateStudentDto: UpdateStudentDto): Promise<IStudent> { const existingStudent = await this.studentModel.findByIdAndUpdate(studentId, updateStudentDto, { new: true }); if (!existingStudent) { throw new NotFoundException(`Student #${studentId} not found`); } return existingStudent; } } |
Registering the Service
Now we also need to register the service inside the app.module.ts by adding it inside the providers array as shownn below
It will be automatically be done for you if you created your service using the command above. The nestjs automatically does it for you. If you create the service manually then you need to register the service by including it inside the providers array as shown above.
Creating the Controller
Now it’s time to create the Controller which will actually contain the four crud requests and methods (GET,POST,PUT,DELETE) to actually use the service to call the methods when user performs any of these requests using this controller. Now first of all create the controller using the below command in terminal
nest generate controller student
And if you execute the command you will see the controller files will also be generated inside the same folder student
.
student.controller.ts
1 2 3 4 5 6 7 |
import { Body, Controller, Delete, Get, HttpStatus, Param, Post, Put, Res } from '@nestjs/common'; import { StudentService } from './student.service'; @Controller('student') export class StudentController { constructor(private readonly studentService: StudentService) { } } |
As you can see in the above code we are importing the studentService from the services folder and also we are also using the @Controller decorator because it is the controller file. And also inside it we will also have the constructor in which we have the studentService as an argument.
Now basically we will write the post http request to create a new Student. Here we are using the @post keyword as shown below. It is used for post request
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Post() async createStudent(@Res() response, @Body() createStudentDto: CreateStudentDto) { try { const newStudent = await this.studentService.createStudent(createStudentDto); return response.status(HttpStatus.CREATED).json({ message: 'Student has been created successfully', newStudent,}); } catch (err) { return response.status(HttpStatus.BAD_REQUEST).json({ statusCode: 400, message: 'Error: Student not created!', error: 'Bad Request' }); } } |
Here as you can see we are having async function having this post request. In the response and body we will be creating a new student. In this function we are wrapping all the code in try and catch block. Here we are calling the method of the student service which is createStudent which is used to create a new student and in the argument we are passing createStudentDto object. And when the student is created a json response is returned to the client. The message says that the student is created successfully. If any error takes place then it will be catched inside the catch block.
Now basically we will write the get request to fetch all the student records. For this we will be calling method which is present inside the service.
1 2 3 4 5 6 7 8 9 10 11 |
@Get() async getStudents(@Res() response) { try { const studentData = await this.studentService.getAllStudents(); return response.status(HttpStatus.OK).json({ message: 'All students data found successfully', studentData, }); } catch (err) { return response.status(err.status).json(err.response); } } |
As in this block of code we have getStudents() method in this we are calling getAllStudents() method present inside the studentService and then we are returning json response to the client.
Now we will be writing the put request to handle the update operation inside the controller file as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Put('/:id') async updateStudent(@Res() response, @Param('id') studentId: string, @Body() updateStudentDto: UpdateStudentDto) { try { const existingStudent = await this.studentService.updateStudent(studentId, updateStudentDto); return response.status(HttpStatus.OK).json({ message: 'Student has been successfully updated', existingStudent, }); } catch (err) { return response.status(err.status).json(err.response); } } |
As you can see we are writing the put request to update the existing student record. In this request we are using the method updateStudent from the service passing the id of the student. And then we are returning the json response to the client.
And then guys we will write the get request to send information about a specific student by getting it’s id. So here in this block of code we are using the service method getStudent() as shown below. And then we are returning the json response.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Get('/:id') async getStudent(@Res() response, @Param('id') studentId: string) { try { const existingStudent = await this.studentService.getStudent(studentId); return response.status(HttpStatus.OK).json({ message: 'Student found successfully', existingStudent, }); } catch (err) { return response.status(err.status).json(err.response); } } |
So now we will writing the delete request to delete the actual student by using it’s id. In this request we are using the service method deleteStudent() and then we are returning the json response.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Delete('/:id') async deleteStudent(@Res() response,@Param('id') studentId:string){ try{ const deletedStudent = await this.studentService.deleteStudent(studentId) return response.status(HttpStatus.OK).json({ message:"Student Deleted Successfully", deletedStudent }) }catch(err){ return response.status(err.status).json(err.response) } } |
Testing the Backend CRUD API in Postman
Now after building the crud rest api we will be testing the api in postman. In postman first of all you need to select the method whether get or post
We will be taking the example of how to create a student by making a post request. Here first of all we need to set the header of content-type to application/json and also we need to pass raw json in body as shown below
As you can see if we send the post request through postman the record is inserted inside the mongodb database and table. As you can see in the above pictures.
Now to get all the students we need to pass a get request to http://localhost:3000/student
Now we will be deleting the user by using it’s id. In postman we are using the method of delete and then we are passing the id in the request as shown below. Also we get a json response that the student record has been deleted successfully.
Now we will be using the put request to update the existing user by using it’s id. And also we are getting a json response from the api that the fields are updated successfully. Also here also we need to set the headers of content-type to application/json and also we need to write the raw json response
Wrapping it up guys thanks very much for reading this blog post. In this post we build nest.js crud rest api from scratch and tested it using postman software. Thanks very much and I will seeing you in another post.