Click to share! ⬇️

Nest.js is a powerful, progressive Node.js framework for building efficient, scalable, and modular server-side applications. It is built with TypeScript, which adds features such as strong typing, interfaces, and decorators to JavaScript. Nest.js leverages the latest features of JavaScript and provides a structured approach to building server-side applications.

Nest.js is heavily inspired by Angular and provides many of the same features, such as dependency injection, modules, and controllers. It is designed to be extensible, meaning that developers can create their own modules and plugins to extend the functionality of Nest.js.

In this tutorial, we will cover the basics of Nest.js and provide a step-by-step guide on how to build a simple server-side application. We will start by installing Nest.js and setting up a basic application. Then, we will explore the different features of Nest.js, such as controllers, services, middleware, and guards. Finally, we will cover how to test and deploy a Nest.js application.

How to Install Nest.js

Before we can start building a Nest.js application, we need to install it. Nest.js can be installed using npm, the Node.js package manager.

To install Nest.js, open a terminal or command prompt and run the following command:

npm install -g @nestjs/cli

This command will install the Nest.js CLI globally on your system. The CLI provides a set of commands that we can use to generate and manage our Nest.js applications.

Once the installation is complete, you can verify that Nest.js has been installed by running the following command:

nest --version

This command will display the version of Nest.js that is currently installed on your system.

Nice Work! You have successfully installed Nest.js on your system. Now we can move on to creating our first Nest.js application.

How to Create a Basic Nest.js Application

Now that we have Nest.js installed, let’s create a basic application. We can do this using the Nest.js CLI.

To create a new Nest.js application, open a terminal or command prompt and navigate to the directory where you want to create the application. Then, run the following command:

nest new my-app

This command will create a new Nest.js application in a directory named my-app. You can replace my-app with the name of your choice.

Once the command is complete, navigate to the my-app directory by running the following command:

cd my-app

Now that we are inside the application directory, we can start the application by running the following command:

npm run start:dev

This command will start the application in development mode. You should see output in the terminal indicating that the application has started.

By default, the application will be running on http://localhost:3000. You can open a web browser and navigate to this URL to see the default Nest.js welcome page.

How to Define Controllers and Routes in Nest.js

In Nest.js, controllers are responsible for handling incoming HTTP requests and returning responses. Controllers can define routes that map to specific URL paths and HTTP methods.

To create a new controller, use the Nest.js CLI by running the following command in your project directory:

nest generate controller cats

This command will create a new controller named cats in a file named cats.controller.ts. Open this file and you will see a basic controller class that looks like this:

import { Controller } from '@nestjs/common';

@Controller('cats')
export class CatsController {}

This controller has a route decorator that specifies the URL path for this controller’s routes. In this case, the controller will handle requests that start with /cats.

To define a route for this controller, add a method to the class and decorate it with the HTTP method decorator that corresponds to the desired HTTP verb. For example, to define a route for a GET request to /cats, add the following method to the CatsController class:

import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

In this example, the @Get() decorator specifies that this method should handle GET requests, and the method returns a string that will be sent in the response.

You can define additional routes for this controller by adding methods with different HTTP method decorators. For example, to define a route for a POST request to /cats, add the following method to the CatsController class:

import { Controller, Get, Post } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }

  @Post()
  create(): string {
    return 'This action adds a new cat';
  }
}

In this example, the @Post() decorator specifies that this method should handle POST requests, and the method returns a string that will be sent in the response.

How to Use Nest.js Services and Providers

In Nest.js, services are used to handle business logic and can be injected into controllers or other services. Providers are the building blocks for services and can be used to create instances of classes, functions, or values that can be injected into other components.

To create a new service, use the Nest.js CLI by running the following command in your project directory:

nest generate service cats

This command will create a new service named cats in a file named cats.service.ts. Open this file and you will see a basic service class that looks like this:

import { Injectable } from '@nestjs/common';

@Injectable()
export class CatsService {}

This service is decorated with the @Injectable() decorator, which tells Nest.js that this class can be injected into other components.

To add functionality to this service, add methods to the class. For example, to add a method that returns a list of cats, add the following method to the CatsService class:

import { Injectable } from '@nestjs/common';

@Injectable()
export class CatsService {
  private cats = ['Tom', 'Garfield', 'Sylvester'];

  findAll(): string[] {
    return this.cats;
  }
}

In this example, the cats array is a private property of the service, and the findAll() method returns this array.

To use this service in a controller, inject the service into the controller’s constructor using the @Inject() decorator. For example, to use the CatsService in the CatsController, modify the controller class as follows:

import { Controller, Get, Post, Body, Inject } from '@nestjs/common';
import { CatsService } from './cats.service';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll(): string[] {
    return this.catsService.findAll();
  }

  @Post()
  create(@Body() body: { name: string }): string {
    const name = body.name;
    this.catsService.create(name);
    return `Added ${name} to the list of cats`;
  }
}

In this example, the CatsService is injected into the CatsController constructor, and the findAll() method is called in the findAll() controller method.

How to Implement Middleware in Nest.js

Middleware functions can be used to intercept requests and responses before they are handled by a controller. Middleware functions can be used for tasks such as authentication, logging, and error handling.

To create a middleware function, create a new TypeScript file in your project directory. This file should export a function that takes three arguments: the request object, the response object, and the next function.

Here’s an example middleware function that logs the request method and URL:

import { Request, Response } from 'express';

export function loggerMiddleware(req: Request, res: Response, next: () => void) {
  console.log(`${req.method} ${req.originalUrl}`);
  next();
}

To use this middleware function in a Nest.js application, add it to the list of middleware functions for the application. To do this, modify the main.ts file in your project directory as follows:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { loggerMiddleware } from './logger.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(loggerMiddleware);
  await app.listen(3000);
}
bootstrap();

In this example, the loggerMiddleware function is added to the list of middleware functions for the application using the app.use() method.

Now, whenever a request is made to the Nest.js application, the loggerMiddleware function will be called before the request is handled by a controller.

How to Connect to a Database with Nest.js

Nest.js provides built-in support for connecting to databases using various database adapters, such as TypeORM, Sequelize, and Mongoose. In this example, we will use TypeORM to connect to a PostgreSQL database.

First, install the required packages using npm by running the following command in your project directory:

npm install --save @nestjs/typeorm typeorm pg

This command will install the necessary packages for connecting to a PostgreSQL database using TypeORM.

Next, create a ormconfig.json file in your project directory with the following contents:

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "postgres",
  "password": "password",
  "database": "nest",
  "entities": ["dist/**/*.entity.js"],
  "synchronize": true
}

This configuration file specifies the details for connecting to a PostgreSQL database with the username and password of “postgres” and “password” respectively. The database name is “nest”, and the entities are defined in all files with the extension .entity.js in the dist/ directory.

Next, create a new TypeScript file in your project directory with the following contents:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [TypeOrmModule.forRoot(), CatsModule],
})
export class AppModule {}

In this example, we import the TypeOrmModule and the CatsModule. The TypeOrmModule.forRoot() method is used to configure the TypeORM connection using the ormconfig.json file.

Finally, modify the cats.service.ts file to use TypeORM to interact with the database. Here’s an example implementation:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cat } from './cat.entity';

@Injectable()
export class CatsService {
  constructor(
    @InjectRepository(Cat)
    private catsRepository: Repository<Cat>,
  ) {}

  async findAll(): Promise<Cat[]> {
    return this.catsRepository.find();
  }

  async create(cat: Cat): Promise<void> {
    await this.catsRepository.save(cat);
  }
}

In this example, the CatsService is modified to use the @InjectRepository() decorator to inject the Repository object for the Cat entity into the service constructor.

Now, the findAll() and create() methods can be modified to use the catsRepository object to interact with the database.

How to Use Nest.js Guards and Interceptors

Nest.js provides guards and interceptors as a way to implement cross-cutting concerns such as authorization, validation, and logging in a modular and reusable way.

Guards are used to implement authorization and can be used to protect routes or controller methods. Interceptors are used to modify incoming requests or outgoing responses and can be used for tasks such as logging, rate limiting, or caching.

To create a new guard, use the Nest.js CLI by running the following command in your project directory:

nest generate guard auth

This command will create a new guard named auth in a file named auth.guard.ts. Open this file and you will see a basic guard class that looks like this:

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

This guard implements the CanActivate interface, which requires the canActivate() method to be implemented. This method takes an ExecutionContext object as an argument and returns a boolean or a Promise or an Observable that resolves to a boolean. The ExecutionContext object contains information about the current request and response.

To use this guard to protect a route, add the @UseGuards() decorator to the controller or method that you want to protect. For example, to protect the create() method in the CatsController, modify the controller class as follows:

import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import { CatsService } from './cats.service';
import { Cat } from './cat.entity';
import { AuthGuard } from './auth.guard';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Post()
  @UseGuards(AuthGuard)
  create(@Body() body: Cat): Promise<void> {
    return this.catsService.create(body);
  }
}

In this example, the @UseGuards() decorator is added to the create() method, and the AuthGuard is passed as an argument. This means that the create() method can only be called if the canActivate() method of the AuthGuard returns true.

To create a new interceptor, use the Nest.js CLI by running the following command in your project directory:

nest generate interceptor logging

This command will create a new interceptor named logging in a file named logging.interceptor.ts. Open this file and you will see a basic interceptor class that looks like this:

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    const now = Date.now();

    return next.handle().pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}

This interceptor implements the NestInterceptor interface, which requires the intercept() method to be implemented. This method takes an ExecutionContext object and a CallHandler object as arguments and returns an Observable. The ExecutionContext object contains information about the current request and response, and the CallHandler object contains a handle() method that returns an Observable that emits the response.

To use this interceptor, add the @UseInterceptors() decorator to the controller or method that you want to intercept. For example, to add the LoggingInterceptor to the CatsController, modify the controller class as follows:

import { Controller, Get, Post, Body, UseGuards, UseInterceptors } from '@nestjs/common';
import { CatsService } from './cats.service';
import { Cat } from './cat.entity';
import { AuthGuard } from './auth.guard';
import { LoggingInterceptor } from './logging.interceptor';

@Controller('cats')
@UseInterceptors(LoggingInterceptor)
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Post()
  @UseGuards(AuthGuard)
  create(@Body() body: Cat): Promise<void> {
    return this.catsService.create(body);
  }
}

In this example, the @UseInterceptors() decorator is added to the CatsController class, and the LoggingInterceptor is passed as an argument. This means that all requests to the CatsController will be intercepted by the LoggingInterceptor.

How to Test a Nest.js Application

Nest.js provides a built-in testing module that allows you to write unit tests for your application’s controllers, services, guards, interceptors, and other components.

To create a new test for a controller, use the Nest.js CLI by running the following command in your project directory:

nest generate test cats

This command will create a new test file named cats.controller.spec.ts in the src directory. Open this file and you will see a basic test suite that looks like this:

import { Test, TestingModule } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let controller: CatsController;

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [CatsController],
      providers: [CatsService],
    }).compile();

    controller = app.get<CatsController>(CatsController);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['Tom', 'Garfield', 'Sylvester'];
      jest.spyOn(controller, 'findAll').mockImplementation(() => result);

      expect(await controller.findAll()).toBe(result);
    });
  });
});

This test suite creates a new instance of the CatsController and a CatsService using the Nest.js testing module, and then tests the findAll() method of the controller.

To run the test, use the following command in your project directory:

npm run test

This command will run all the tests in the src directory and display the results in the console.

Excellent! You have written and run a unit test for a Nest.js controller. In the same way, you can also write tests for services, guards, interceptors, and other components of your Nest.js application.

How to Extend Nest.js with Plugins and Modules

Nest.js allows you to extend your application’s functionality by using plugins and modules. Plugins are reusable pieces of functionality that can be easily integrated into your application, while modules are collections of components that can be used to organize your application into logical units.

To create a new plugin, create a new TypeScript file in your project directory that exports a class or a function. For example, to create a logger plugin that logs all requests and responses, create a logger.plugin.ts file with the following contents:

import { Injectable } from '@nestjs/common';

@Injectable()
export class LoggerPlugin {
  onRequest(req, res) {
    console.log(`${req.method} ${req.originalUrl}`);
  }

  onResponse(req, res) {
    console.log(`${req.method} ${req.originalUrl} ${res.statusCode}`);
  }
}

This class defines an onRequest() method that logs the request method and URL, and an onResponse() method that logs the request method, URL, and status code.

To use this plugin in your application, create a new instance of the LoggerPlugin and add it to the list of middleware functions for the application using the app.use() method. For example, modify the main.ts file in your project directory as follows:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggerPlugin } from './logger.plugin';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use((req, res, next) => {
    const logger = new LoggerPlugin();
    logger.onRequest(req, res);
    res.on('finish', () => logger.onResponse(req, res));
    next();
  });
  await app.listen(3000);
}
bootstrap();

In this example, the LoggerPlugin is used as middleware for the application. The onRequest() method is called when a request is made to the application, and the onResponse() method is called when a response is sent back to the client.

To create a new module, use the Nest.js CLI by running the following command in your project directory:

nest generate module cats

This command will create a new module named CatsModule in a file named cats.module.ts. Open this file and you will see a basic module class that looks like this:

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

This module defines a CatsController and a CatsService, which can be used to handle requests and implement business logic related to cats.

To use this module in your application, import it into the AppModule and add it to the imports array. For example, modify the app.module.ts file in your project directory as follows:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CatsModule } from './cats/cats.module';
import { Cat } from './cats/cat.entity';

@Module({
  imports: [TypeOrmModule.forRoot(), CatsModule],
  providers: [],
})
export class AppModule {}

In this example, the CatsModule is imported and added to the imports array. This means that the CatsController and CatsService can be used in the application.

Summary

In this tutorial, we have learned how to create a basic Nest.js application, define controllers and routes, use services and providers, implement middleware, connect to a database, use guards and interceptors, test the application, and extend it with plugins and modules.

We started by installing Nest.js and creating a basic application with a controller and a service. We then defined routes for the controller and used services to handle business logic. We also implemented middleware to handle cross-cutting concerns such as logging and validation.

We learned how to connect to a database using TypeORM, and how to use guards and interceptors to implement authorization and other cross-cutting concerns. We also learned how to write and run unit tests for our application.

Finally, we extended our application with a logger plugin and a cats module. Plugins and modules allow us to easily add new functionality to our application and organize our code into logical units.

With these skills, you should be able to build complex, scalable, and maintainable Node.js applications using Nest.js.

Click to share! ⬇️