In this article, I’m going to walk you through developing a simple todo rest application in NestJS and give you an overview of this framework. What’s NestJS? NestJS is a framework for a fast development of backend applications whose structure, inspired from Angular, makes it perfect for the realization of projects with MEAN stack. https://nestjs.com/ When to use NestJS? Nest is most suitable for developing applications that manage a huge quantity of requests with a tiny amount of data processed on the server side and prompt response time (e-commerce, live chats, etc..). Why use NestJS? The benefits of using NestJS are innumerable. The most important (imho) to highlight are: : with Typescript we exploit all the power of javascript without running into type problems. Type checking : through the division into modules we can isolate our components based on the domain boundary. Modular : thanks to the DI container we are able to write decoupled and testable code. Dependency Injection : Nest integrates all the most used and community tested javascript / typescript libraries and provides all the tools developers need to contribute to the collection. Adapters : the familiar structure allows us to easily switch from frontend to backend without having to change the development approach every time. Angular Like : we can develop our functionality without having to worry about the initial boilerplate. CLI Let’s start writing code retrieve the list of all todos retrieve a single todo insert a todo edit a todo delete a todo therefore of the following APIs: [GET] todos [GET] todos/{id} [POST] todos [PUT] todos/{id} [DELETE] todos/{id} step 0: Setup Setting up our project with two simple commands in terminal: npm i -g @nestjs/cli nest new TodoBackend Run in todo-backend folder to make sure everything is okay! npm run start step 1: Module Modules are the mechanism that allows us to separate our components based on a domain they belong to and, in practice, to instruct the DI container about their interactions in the bootstrap phase. The main module with which the bootstrap is performed is called root module and, in applications generated via CLI, we find it in the src folder under the AppModule name. Our application, being very small and with a single functionality, could directly exploit the root module to manage its dependencies. There are at least two good reasons we won’t be doing this in this guide: It’s not a typical case and consequently difficult to apply in a real context. We would not be decoupled, thus losing the maintainability and portability of our code. We create our module via CLI: nest generate module todo ({ imports: [], providers: [], controllers: [] }) TodoModule {} @Module export class When it’s generated, the CLI will also take care of updating the AppModule by importing TodoModule as a feature module 🎉 step 2: Entity An entity is a class that maps a table (or collection) of our database (very pragmatic definition). We create our entity through CLI: nest generate class todo/entities/Todo --no-spec Todo { id: ; title: ; completed: ; ( ) { .title = title; .completed = ; } } export class public number public string public boolean public constructor title: string this this false step 3: Repository Now that we have our entity we just have to persist it through an ! ORM For this guide I have decided to use and setting up a basic connection to an database Typeorm sqlite First we install the dependencies: npm i @nestjs/typeorm typeorm sqlite3 We modify AppModule by importing with the static method , inserting the configurations we need: TypeOrmModule forRoot { Module } ; { TypeOrmModule } ; * path ; { TodoModule } ; ({ imports: [ TypeOrmModule.forRoot({ : , autoLoadEntities: , synchronize: , database: path.resolve(__dirname, , ) }), TodoModule ] }) AppModule {} import from '@nestjs/common' import from '@nestjs/typeorm' import as from 'path' import from './todo/todo.module' @Module type 'sqlite' true true '..' 'db.sqlite' export class Let’s add also on , this time using the method, specifying Todo as the entity to manage: TypeOrmModule TodoModule forFeature { Module } ; { TypeOrmModule } ; { Todo } ; ({ imports: [ TypeOrmModule.forFeature([Todo]) ], providers: [], controllers: [] }) TodoModule {} import from '@nestjs/common' import from '@nestjs/typeorm' import from './entities' @Module export class Now that we’ve configured Typeorm we can finally update our Todo entity with all the necessary annotations: { Entity, Column, PrimaryGeneratedColumn } ; () Todo { () id: ; () title: ; () completed: ; ( ) { .title = title; .completed = ; } } import from 'typeorm' @Entity export class @PrimaryGeneratedColumn public number @Column public string @Column public boolean public constructor title: string this this false You can read further informations about and its annotations by consulting the link attached at the beginning of the step. Typeorm For and methods, you can consult the database section in the official NestJS documentation: TypeOrmModule forRoot forFeature https://docs.nestjs.com/techniques/database step 4: DTO To avoid exposing our entities outside our business logic layer, we define a set of classes that will be used to manage the communication in and out of our services: the ( ata ransfer bjects). DTO D T O AddTodoDto { readonly title: ; ( ) { .assign( , opts); } } export class public string public constructor opts?: Partial<AddTodoDto> Object this EditTodoDto { readonly title: ; readonly completed: ; ( ) { .assign( , opts); } } export class public string public boolean public constructor opts?: Partial<EditTodoDto> Object this { public readonly id: number; public readonly title: string; public readonly completed: boolean; public (opts?: Partial<TodoDto>) { .assign( , opts); } } export class TodoDto constructor Object this step 5: Service The service is the “package” where we are going to encapsulate our business logic, exposing a set of ready-to-use features. Let’s define the objects that will populate our service layer: nest generate service todo/services/todo In the created service, we implement the , , , and methods which, through the DTOs, will be consumed by our controller. findAll findOne add edit delete To decouple the conversion logic from Entity to DTO (and vice versa) from the business logic, let’s create a TodoMapperService: nest generate service todo/services/TodoMapper { Injectable } ; { Todo } ; { TodoDto } ; () TodoMapperService { modelToDto({ id, title, completed }: Todo): TodoDto { TodoDto({ id, title, completed }); } } import from '@nestjs/common' import from '../../entities' import from '../../dto' @Injectable export class public return new Now let’s implement our : we inject, through Dependency Injection, the Todo Repository provided by and our : TodoService Typeorm TodoMapperService { isNullOrUndefined } ; { Injectable, NotFoundException } ; { Repository } ; { InjectRepository } ; { Todo } ; { TodoDto, AddTodoDto, EditTodoDto } ; { TodoMapperService } ; () TodoService { ( ) {} findAll(): <TodoDto[]> { todos = .todoRepository.find(); todos.map( .todoMapper.modelToDto); } findOne(id: ): <TodoDto> { todo = .todoRepository.findOne(id); (isNullOrUndefined(todo)) NotFoundException(); .todoMapper.modelToDto(todo); } add({ title }: AddTodoDto): <TodoDto> { todo = Todo(title); todo = .todoRepository.save(todo); .todoMapper.modelToDto(todo); } edit(id: , { title, completed }: EditTodoDto): <TodoDto> { todo = .todoRepository.findOne(id); (isNullOrUndefined(todo)) NotFoundException(); todo.completed = completed; todo.title = title; todo = .todoRepository.save(todo); .todoMapper.modelToDto(todo); } remove(id: ): <Todo> { todo = .todoRepository.findOne(id); (isNullOrUndefined(todo)) NotFoundException(); todo = .todoRepository.remove(todo); todo; } } import from 'util' import from '@nestjs/common' import from 'typeorm' import from '@nestjs/typeorm' import from '../../entities' import from '../../dto' import from '../todo-mapper/todo-mapper.service' @Injectable export class public constructor (Todo) readonly todoRepository: Repository<Todo>, readonly todoMapper: TodoMapperService @InjectRepository private private public async Promise const await this return this public async number Promise const await this if throw new return this public async Promise let new await this return this public async number Promise let await this if throw new await this return this public async number Promise let await this if throw new await this return step 6: Controller Here we are at the last layer of our climb to the NestJS stack! ⛰ To create our controller we will use our very useful CLI for the last time with this command: nest generate controller todo/controllers/todo Let’s implement the methods that will mirror the rest calls we listed at the beginning of the article, decorate them with routing annotations and hook them to the TodoService methods: { TodoService } ; { TodoDto, AddTodoDto, EditTodoDto } ; { Controller, Get, Param, Post, Put, Body, Delete } ; @Controller( ) { public (private readonly todoService: TodoService) {} @Get() public findAll(): <TodoDto[]> { .todoService.findAll(); } @Get( ) public findOne(@Param( ) id: number): <TodoDto> { .todoService.findOne(id); } @Put( ) public edit(@Param( ) id: number, @Body() todo: EditTodoDto): <TodoDto> { .todoService.edit(id, todo); } @Post() public add(@Body() todo: AddTodoDto): <TodoDto> { .todoService.add(todo); } @Delete( ) public remove(@Param( ) id: number): <TodoDto> { .todoService.remove(id); } } import from './../services/todo/todo.service' import from './../dto' import from '@nestjs/common' 'todos' export class TodoController constructor Promise return this ':id' 'id' Promise return this ':id' 'id' Promise return this Promise return this ':id' 'id' Promise return this DTO serialization is not active unless you decorate your controller method with WARNING: ClassSerializerInterceptor @Post() @UseInterceptors(ClassSerializerInterceptor) public add(@Body() todo: AddTodoDto): Promise<TodoDto> { In the next step we will deepen this topic by developing a solution that allows us to centralize this interceptor 😉 step 7: Validation Our DTOs are ready to travel as fast as Formula 1 cars under the http protocol but one last piece is missing: the validation of its data. To handle the validation of our fields, NestJS provides a validation that takes advantage of the class-transformer and class-validator libraries. To be able to use it however, we need to install its dependencies in the project: pipe npm i class-transformer class-validator Let’s add the ValidationPipe to the global pipes: { NestFactory } ; { AppModule } ; { ValidationPipe } ; { app = NestFactory.create(AppModule); app.useGlobalPipes( ValidationPipe({ : })); app.listen( ); } bootstrap(); import from '@nestjs/core' import from './app.module' import from '@nestjs/common' async ( ) function bootstrap const await new transform true await 3000 And now let’s decorate our DTOs: { IsNotEmpty } ; EditTodoDto { () readonly title: ; readonly completed: ; ( ) { .assign( , opts); } } import from 'class-validator' export class @IsNotEmpty public string public boolean public constructor opts?: Partial<EditTodoDto> Object this : Once our application has been compiled, all the DTOs we have defined so far will be converted into javascript objects, this means that no type check will be performed on the values of its fields! WARNING So will our validators only work as long as they are passed the right type values? . NO The class-validator library also has a set of validators specifically designed to type check our fields at runtime: { IsBoolean, IsNotEmpty, IsString } ; EditTodoDto { () () readonly title: ; () readonly completed: ; ( ) { .assign( , opts); } } import from 'class-validator' export class @IsString @IsNotEmpty public string @IsBoolean public boolean public constructor opts?: Partial<EditTodoDto> Object this step 8: Run! It’s time to run our application! 🎉 To execute it normally, just run the command: npm run start If we need to debug our code, we will have to run the command: npm run start:debug step 9: Enable CORS After developing our backend, we will most likely want to develop a frontend application and try to consume our todo-backend, here the CORS comes into play: (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one , access to selected resources from a different origin. […] For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. For example, XmlHttpRequest and the Fetch API follow the same-origin policy. Cross-Origin Resource Sharing origin https://developer.mozilla.org/en/docs/Web/HTTP/CORS To enable CORS just edit main.ts again by invoking the method with all the configuration parameters we need. For simplicity, we will enable everything! (Freedom! 🤟) enableCors() { NestFactory } ; { AppModule } ; { ValidationPipe } ; { app = NestFactory.create(AppModule); app.useGlobalPipes( ValidationPipe({ transform: })); app.enableCors(); app.listen( ); } bootstrap(); import from '@nestjs/core' import from './app.module' import from '@nestjs/common' async ( ) function bootstrap const await new true await 3000 step 10: Test Tests are absolutely necessary when you want to build a maintainable application: they prevent us from the most common development errors and possible regressions due to the modification of an existing functionality. When we generate a new component via CLI, its test file is also automatically generated. Let’s try to make one: { Test, TestingModule } ; { TodoService } ; { getRepositoryToken } ; { Todo } ; { repositoryMockFactory, MockType } ; { TodoMapperService } ; { Repository } ; describe( , { service: TodoService; repository: MockType<Repository<Todo>>; beforeEach( () => { : TestingModule = await Test.createTestingModule({ providers: [ TodoService, TodoMapperService, { provide: getRepositoryToken(Todo), useFactory: repositoryMockFactory } ], }).compile(); repository = . <Repository<Todo>>(getRepositoryToken(Todo)) unknown MockType<Repository<Todo>>; service = . <TodoService>(TodoService); }); it( , { expect(service).toBeDefined(); }); it( , () => { repository.findOne.mockReturnValue( .resolve( )); expect(service.findOne( )).rejects.toThrow( ); }); }); import from '@nestjs/testing' import from './todo.service' import from '@nestjs/typeorm' import from './../../entities' import from './../../../utils/test/repository.mock' import from './../todo-mapper/todo-mapper.service' import from 'typeorm' 'TodoService' => () let let async const module module get as as module get 'should be defined' => () 'should throws exception when todo not exists' async Promise null await 1 'Not Found' For test development, Nest uses under the hoo Jest for more information see the NestJS documentation https://docs.nestjs.com/fundamentals/testing You can find the sources of this article on my Github repository, feel free to put a ⭐️ 😁 https://github.com/KernelPanic92/nestjs-todo-ap Conclusion By creating the application described in this article, I was able to see firsthand how fast and powerful this framework is: it allows you to be extremely fast and flexible without having to give up absolutely anything. NestJS passes exams with top votes and can deservedly occupy its place in the top list of frameworks for web development. If you enjoyed this guide, don’t forget to 👏 and share it with all the people who might benefit from it 😃 So long, and thanks for all the fish 🐬 Photo by Sereja Ris on Unsplash