Hello guys! In this article, we can make crud APIs in NestJs. if you are unfamiliar with NestJS, let me tell you shortly.
NestJS is a framework for NodeJS to build server-side applications, under the hood by default NestJS uses the express, if required we can also change the platform to Fastify. NestJS provides an abstraction to these frameworks like Express and Fastify.
We can set up the Nest with cli, it will generate the files and basic code. but we don't need that we can set it up from scratch.
npm i --save @nestjs/core @nestjs/common rxjs reflect-metadata
run the above command to initialize the project.
this initializes the project, also we need to install additional libraries
npm install @nestjs/platform-express @nestjs/mongoose ts-node typescript nodemon
in the package.json
add the scripts to run the server.
"scripts": {
"start": "ts-node main.ts",
"dev": "nodemon --exec ts-node main.ts"
},
we use the ts-node
to run the typescript files directly on the node.
Let's start the project,
Project structure: this is my folder structure
CRUD/
│
├── node_modules/
│
├── schemas/
│ └── todo.schema.ts
│
├── todo/
│ ├── todo.controller.ts
│ ├── todo.module.ts
│ ├── todo.service.ts
│ └── todo.types.ts
│
├── .gitignore
├── app.module.ts
├── main.ts
├── package-lock.json
├── package.json
└── tsconfig.json
before that, we need to config in the tsconfig.json
create a file tsconfig.json
and add the following,
the experimentalDecorators
and emitDecoratorMetadata
allow us to use the Decorators in the ts, if you are unfamiliar with the decorators, let me tell you the high-level definition, "decorators are used to modifying the behavior of a class without modifying the underlying code".
create a main.ts
and app.module.ts
In the app.module.ts
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { TodoModule } from "./todo/todo.module";
@Module({
imports: [
MongooseModule.forRoot("mongodb://127.0.0.1:27017/nest"),
TodoModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
In the main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function Server() {
const app = await NestFactory.create(AppModule);
await app.listen(4000); //server listen on port 4000
}
Server();
The good thing about NestJS is the dependency injection, we can configure the dependency in the module, and inject it across the app and the instance will be created automatically by the nest.
let's define a schema for the database in the todo.schema.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { HydratedDocument } from "mongoose";
export type TodoDoc = HydratedDocument<Todo>;
@Schema()
export class Todo {
@Prop({ required: true })
name!: string;
@Prop({ required: true })
content!: string;
}
export const TodoSchema = SchemaFactory.createForClass(Todo);
we can also define the schema without decorators, as we do in the express application but let's try with the decorators.
Let's create a separate directory for called todo, here we will handle the service, controller, and module regarding todos.
In the todo/todo.module.ts
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { Todo, TodoSchema } from "../schemas/todo.schema";
import { TodoController } from "./todo.controller";
import { TodoService } from "./todo.service";
@Module({
imports: [
MongooseModule.forFeature([{ name: Todo.name, schema: TodoSchema }]),
],
controllers: [],
providers: [],
})
export class TodoModule {}
The MongooseModule.forFeature()
registers the schema in NestJS's dependency injection system, making the TodoModel
available throughout the app. For now, let's keep the controller's array and the provider's array empty, once we define the service and controllers we can add it here.
Let's make a service, create a file in todo/todo.service.ts
import { Model } from "mongoose";
import { Injectable } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Todo } from "../schemas/todo.schema";
import { todo } from "./todo.types";
@Injectable()
export class TodoService {
constructor(@InjectModel(Todo.name) private todoModel: Model<Todo>) {}
async create(data: todo): Promise<Todo> {
try {
const result = new this.todoModel(data);
return await result.save();
} catch (error) {
throw error;
}
}
async getAll(): Promise<Todo[]> {
try {
const data = await this.todoModel.find();
if (!data) {
throw new Error("No data");
}
return data;
} catch (error) {
throw error;
}
}
async deleteTodo(id: string): Promise<Todo> {
try {
const data = await this.todoModel.findByIdAndDelete(id);
if (!data) {
throw new Error("error in deletion");
}
return data;
} catch (error) {
throw error;
}
}
async updateTodo(id: string, upDate: todo): Promise<Todo> {
try {
const data = await this.todoModel.findByIdAndUpdate(id, upDate, {
new: true,
});
if (!data) {
throw new Error("can't update");
}
return data;
} catch (error) {
throw error;
}
}
}
These are the crud services for our API.
-
@Injectable()
: This is a decorator, that makes our service to be used in the controller, with injection. NestJS provides a powerful Dependency Injection system that automatically resolves and manages dependencies across the application. The @Injectable() decorator marks a class as a provider, and NestJS injects these services where needed without requiring the manual instantiation.
constructor(@InjectModel(Todo.name) private todoModel: Model<Todo>) {}
//the above like Inject the model into our service, so we can use the Mongo methods in it.
remaining are just regular methods for crud operations. As we see that every method returns a Promise<Todo>
, it will resolve the Todo
structure promise.
Now, update the todo.module.ts
Providers array,
@Module({
imports: [
MongooseModule.forFeature([{ name: Todo.name, schema: TodoSchema }]),
],
controllers: [],
providers: [TodoService],
})
Now the TodoService is available for injection, we will inject it into the controllers and use those methods.
Let's Now create a controller todo/todo.controller.ts
import {
Body,
Controller,
Get,
Post,
Req,
Res,
Param,
Delete,
Patch,
} from "@nestjs/common";
import { Request, Response } from "express";
import { TodoService } from "./todo.service";
import { todo } from "./todo.types";
@Controller("todo")
export class TodoController {
constructor(private todoService: TodoService) {}
//create a new data
@Post("/create")
async create(@Body() body: todo, @Res() res: Response) {
const data = body;
try {
const result = await this.todoService.create(data);
res.status(200).json(result);
} catch (error) {
return res.status(500).json({ error: (error as Error).message });
}
}
//get all the data
@Get()
async getAll(@Res() res: Response) {
try {
const result = await this.todoService.getAll();
return res.status(200).json(result);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
}
//delete a record
@Delete("/delete/:id")
async deleteTodo(@Param("id") id: string, @Res() res: Response) {
try {
const result = await this.todoService.deleteTodo(id);
res.status(200).json(result);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
}
//update the record
@Patch("/update/:id")
async updateTodo(
@Param("id") id: string,
@Body() body: todo,
@Res() res: Response
) {
try {
const result = await this.todoService.updateTodo(id, body);
res.status(200).json(result);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
}
}
Controllers will handle the request and response.
with the @Controller
decorator, it indicates that the below class will handle the incoming requests. when we give the @Controller("todo")
the API will prefixed with todo, Now we can write a code for curd routes.
As we can see the Request
and Response
are from the express, we are using the @Req
and @Res
decorators with the Request
and Response
types.
Now we can handle the request and the response like we handle in express.
Now update the todo.module.ts
@Module({
imports: [
MongooseModule.forFeature([{ name: Todo.name, schema: TodoSchema }]),
],
controllers: [TodoController],
providers: [TodoService],
})
these are the API endpoints
-- Get: localhost:4000/todo/
-- POST: localhost:4000/todo/create
-- Patch: localhost:4000/todo/update/:id
--Delete: localhost:4000/todo/delete/:id
You can test it in the insomnia
or postman
For the full code check out my repo: nestjs-crud
if there are any mistakes, comment below
Thank You!!!