🖍️
Developer Note
  • Welcome
  • Git
    • Eslint & Prettier & Stylelint & Husky
  • Programming Language
    • JavaScript
      • Script Async vs Defer
      • Module
      • Const VS Let VS Var
      • Promise
      • Event Loop
      • Execution Context
      • Hoisting
      • Closure
      • Event Buddling and Capturing
      • Garbage Collection
      • This
      • Routing
      • Debounce and Throttle
      • Web Component
      • Iterator
      • Syntax
      • String
      • Array
      • Object
      • Proxy & Reflect
      • ProtoType
      • Class
      • Immutability
      • Typeof & Instanceof
      • Npm (Node package manager)
    • TypeScript
      • Utility Type
      • Type vs Interface
      • Any vs Unknown vs Never
      • Void and undefined
      • Strict Mode
      • Namespace
      • Enum
      • Module
      • Generic
    • Python
      • Local Development
      • Uv
      • Asyncio & Event loop
      • Context Manager
      • Iterator & Generator
      • Fast API
      • Pydantic & Data Class
    • Java
      • Compilation and Execution
      • Data Type
      • Enumeration
      • Data Structure
      • Try Catch
      • InputStream and OutputStream
      • Concurrent
      • Unicode Block
      • Build Tools
      • Servlet
      • Java 8
  • Coding Pattern
    • MVC vs MVVM
    • OOP vs Functional
    • Error Handling
    • MVC vs Flux
    • Imperative vs Declarative
    • Design Pattern
  • Web Communication
    • REST API
      • Web Hook
      • CORS issue
    • HTTPS
    • GraphQL
      • REST API vs GraphQL
      • Implementation (NodeJS + React)
    • Server-Sent Event
    • Web Socket
    • IP
    • Domain Name System (DNS)
  • Frontend
    • Progressive Web App (PWA)
    • Single Page & Multiple Page Application
    • Search Engine Optimiaztion (SEO)
    • Web bundling & Micro-frontend
      • Webpack
        • Using Webpack to build React Application
        • Using Webpack to build react library
      • Vite
      • Using rollup to build react library
      • Implementing micro frontend
    • Web Security
      • CSRF & Nonce
      • XSS
      • Click hijacking
    • Cypress
    • CSS
      • Core
        • Box Model
        • Inline vs Block
        • Flexbox & Grid
        • Pseudo Class
        • Position
      • Tailwind CSS
        • Shadcn
      • CSS In JS
        • Material UI
    • React
      • Core
        • Component Pattern
        • React Lazy & Suspense
        • React Portal
        • Error Boundary
        • Rendering Methods
        • Environment Variable
        • Conditional CSS
        • Memo
        • Forward Reference
        • High Order Component (HOC) & Custom Hook
        • TypeScript
      • State Management
        • Redux
        • Recoil
        • Zustand
      • Routing
        • React Router Dom
      • Data Fetching
        • Axios & Hook
        • React Query
        • Orval
      • Table
        • React Table
      • Form & Validation
        • React Hook Form
        • Zod
      • NextJS
        • Page Router
        • App Router
      • React Native
    • Angular
    • Svelte
      • Svelte Kit
  • Backend
    • Cache
      • Browser Cache
      • Web Browser Storage
      • Proxy
      • Redis
    • Rate limit
    • Monitoring
      • Logging
      • Distributed Tracing
    • Load Test
    • Encryption
    • Authentication
      • Password Protection
      • Cookie & Session
      • JSON Web Token
      • SSO
        • OAuth 2.0
        • OpenID Connect (OIDC)
        • SAML
    • Payment
      • Pre-built
      • Custom
    • File Handling
      • Upload & Download (Front-end)
      • Stream & Buffer
    • Microservice
      • API Gateway
      • Service Discovery
      • Load Balancer
      • Circuit Breaker
      • Message Broker
      • BulkHead & Zipkin
    • Elastic Search
    • Database
      • SQL
        • Group By vs Distinct
        • Index
        • N + 1 problem
        • Normalization
        • Foreign Key
        • Relationship
        • Union & Join
        • User Defined Type
      • NOSQL (MongoDB)
      • Transaction
      • Sharding
      • Lock (Concurrency Control)
    • NodeJS
      • NodeJS vs Java Spring
      • ExpressJS
      • NestJS
        • Swagger
        • Class Validator & Validation Pipe
        • Passport (Authentication)
      • Path Module
      • Database Connection
        • Integrating with MYSQL
        • Sequalize
        • Integrating with MongoDB
        • Prisma
        • MikroORM
        • Mongoose
      • Streaming
      • Worker Thread
      • Passport JS
      • JSON Web Token
      • Socket IO
      • Bull MQ
      • Pino (Logging)
      • Yeoman
    • Spring
      • Spring MVC
      • Spring REST
      • Spring Actuator
      • Aspect Oriented Programming (AOP)
      • Controller Advice
      • Filter
      • Interceptor
      • Concurrent
      • Spring Security
      • Spring Boot
      • Spring Cloud
        • Resilience 4j
      • Quartz vs Spring Batch
      • JPA and Hibernate
      • HATEOS
      • Swagger
      • Unit Test (Java Spring)
      • Unit Test (Spring boot)
  • DevOp
    • Docker
    • Kubernetes
      • Helm
    • Nginx
    • File System
    • Cloud
      • AWS
        • EC2 (Virtual Machine)
        • Network
        • IAM
          • Role-Service Binding
        • Database
        • Route 53
        • S3
        • Message Queue
        • Application Service
        • Serverless Framework
        • Data Analysis
        • Machine Learning
        • Monitoring
        • Security
      • Azure
        • Identity
        • Compute Resource
        • Networking
        • Storage
        • Monitoring
      • Google Cloud
        • IAM
          • Workload Identity Federation
        • Compute Engine
        • VPC Network
        • Storage
        • Kubernetes Engine
        • App Engine
        • Cloud function
        • Cloud Run
        • Infra as Code
        • Pub/Sub
    • Deployment Strategy
    • Jenkins
    • Examples
      • Deploy NextJS on GCP
      • Deploy Spring on Azure
      • Deploy React on Azure
  • Domain Knowledge
    • Web 3
      • Blockchain
      • Cryptocurrency
    • AI
      • Prompt
      • Chain & Agent
      • LangChain
      • Chunking
      • Search
      • Side Products
Powered by GitBook
On this page
  • Introduction
  • Module
  • App Module
  • Controller
  • Provider
  • Import & Export
  • Middleware
  • Exception Filter
  • Pipe
  • Guard
  • Interceptor
  • Decorator
  • Sequence
  • Unit Test

Was this helpful?

  1. Backend
  2. NodeJS

NestJS

PreviousExpressJSNextSwagger

Last updated 17 days ago

Was this helpful?

Introduction

  • It is a framework for building efficient, scalable server-side applications.

  • It provides the ecosystem of library, such as mongoose, ...

  • It provides an out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications. The architecture is heavily inspired by Angular. It effectively solve the main problem of - Architecture.

  • It is built around the strong design pattern commonly known as Dependency injection.

  • The dependencies injected can be used within the module

  • Applied AOP design pattern into it, which is similar with spring boot

constructor(private catsService: CatsService) {}
  • Nest is able to work with any Node HTTP framework once an adapter is created. There are two HTTP platforms supported out-of-the-box: and .

  • Whichever platform is used, it exposes its own application interface. These are seen respectively as NestExpressApplication and NestFastifyApplication.

    When you pass a type to the NestFactory.create() method, as in the example below, the app object will have methods available exclusively for that specific platform.

//express
const app = await NestFactory.create<NestExpressApplication>(AppModule);

// express - override version
const server = express();

server.set('query parser', (queryString) => {
  const qs = require('qs');
  return qs.parse(queryString, { 
    depth: 10,  // Increase depth limit from default 5
    arrayLimit: 100,  // Optional: increase array items limit
    parameterLimit: 2000  // Optional: increase parameter count limit
  });
});
const app = await NestFactory.create(
  AppModule, 
  new ExpressAdapter(server),
  { bufferLogs: true }
);

// fastify
const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter()
  );

Module

App Module

  • Each application has at least one module, a app module.

  • The AppModule is the entry point for the application. When the NestJS application starts, it looks for the AppModule to initialize and configure the application. Nest uses to build the application graph

  • For the module having controller should be imported into app module to ensures that the controllers are registered with the NestJS application and that their routes are accessible.

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // Apply global middleware
  app.use(someMiddleware());
  await app.listen(3000);
}
bootstrap();
app.module.ts
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}
  • It can be used to provide global guard, filter, interceptor

app.module.ts
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
    {
      provide: APP_GUARD,
      useClass: StaartAuthGuard,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: AuditLogger,
    },
  ]
})
  • There are 2 methods to define intercepter, guard globally

  • one is to apply in main.ts directly , the guard , intercepter class cannot inject other dependencies into it , since it is registered outside of any module

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { RolesGuard } from './guards/roles.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalGuards(new RolesGuard()); // Apply the guard globally
  await app.listen(3000);
}
bootstrap();

Controller

  • To handle and receive incoming HTTP requests and send responses to the client.

  • Using this built-in method, when a request handler returns a JavaScript object or array, it will automatically be serialized to JSON. When it returns a JavaScript primitive type (e.g., string, number, boolean), however, Nest will send just the value without attempting to serialize it. This makes response handling simple: just return the value, and Nest takes care of the rest.

Provider

  • Many of the basic Nest classes may be treated as a provider – services, repositories, factories, helpers, and so on. The main idea of a provider is that the class that can be injected as a dependency

  • If A service want to get B Service from provider, and B service is also have the relationship with C Service, C must be injected to A Service

@Module({
  imports: [],
  controllers: [DogController],
  providers: [DogService, MouseService],
  exports: [DogService],
})
export class DogModule {}
@Module({
   // choice 1
   // imports: [DogModule], // correct
  controllers: [CatController],
  // providers: [CatService, DogService], // incorrect
  // providers: [CatService, DogService, MouseService] // correct
  exports: [CatService],
})
export class CatModule {}
  • For each time the class is injected by provider, the class will be instantiated for each time

Import & Export

  • Import is the list of imported modules that export the providers which are required in this module

  • Export can the subset of provider or import of this module, so that other module can inject of the exported class after importing

@Module({
  imports: [MouseModule, DragonModule],
  controllers: [DogController],
  providers: [DogService, TreeService],
  exports: [DogService, MouseMoudle],
})
export class DogModule {}
@Module({
  imports: [DogModule],
  controllers: [CatController],
  providers: [CatService],
})
export class CatModule {}
  • In the above example. the class included in dragon module and dog service can be injected into cat controller class, as dog module exported part included it and be imported

  • However, the circular dependencies relationship should be prohibited, here is an example:

@Module({
  imports: [CatModule],
  controllers: [DogController],
  providers: [DogService],
  exports: [DogService],
})
export class DogModule {}
@Module({
  imports: [DogModule],
  controllers: [CatController],
  providers: [CatService],
  exports: [CatService]
})
export class CatModule {}
  • The service class injected from import, the class will only be instantiate for one time only in global level

Middleware

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}

Exception Filter

  • Nest comes with a built-in exceptions layer which is responsible for processing all unhandled exceptions across an application. When an exception is not handled by your application code,

  • It can help to customize the error response depend on the kind of exception thrown

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  BadRequestException,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(BadRequestException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './exceptionHandler/http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

Pipe

  • transformation: transform input data to the desired form (e.g., from string to integer)

  • validation: evaluate input data and if valid, simply pass it through unchanged; otherwise, throw an exception

@Controller('cat')
export class CatController {
  constructor(
  ) {}

  @Get(':id')
  pipeTest(@Param('id', OptionalIntPipe) id: any) {
    return id;
  }
}
export class OptionalIntPipe implements PipeTransform {
  transform(value: string, metadata: ArgumentMetadata): number | undefined {
    if (!value) return undefined;
    const num = Number(value);
    if (isNaN(num))
      throw new BadRequestException(
        OPTIONAL_INT_PIPE_NUMBER.replace('$key', metadata.data),
      );
    return num;
  }
}

Guard

  • Guards have a single responsibility. They determine whether a given request will be handled by the route handler or not, depending on certain conditions (like permissions, roles, ACLs, etc.) present at run-time. This is often referred to as authorization

  • Every guard must implement a canActivate() function. This function should return a boolean, indicating whether the current request is allowed or not.

  • The canActivate() function takes a single argument, the ExecutionContext instance. The ExecutionContext inherits from ArgumentsHost. ArgumentsHost simply acts as an abstraction over a handler's arguments. For example, for HTTP server applications (when @nestjs/platform-express is being used), the host object encapsulates Express's [request, response, next] array

  • Guards have access to the ExecutionContext instance, and thus know exactly what's going to be executed next.

  • Can be combined with decorator for authentication

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

export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes);
@Injectable()
export class ScopesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const scopes = this.reflector.get<string[]>('scopes', context.getHandler());
    const request = context.switchToHttp().getRequest<UserRequest>();
    if (!scopes) return true;
    const user: AccessTokenParsed = request.user;
    let authorized = false;
    if (!user) return false;
    for (const userScope of user.scopes) {
      for (let scope of scopes) {
        for (const key in request.params)
          scope = scope.replace(`{${key}}`, request.params[key]);
        authorized = authorized || minimatch(scope, userScope);
        if (authorized) return true;
      }
    }
    return authorized;
  }
}
@Module({
  providers: [{ provide: APP_GUARD, useClass: TestGuard }],
  imports: [CatModule, DogModule],
})
export class AppModule {}

Interceptor

  • bind extra logic before / after method execution

  • transform the result returned from a function

  • transform the exception thrown from a function

  • extend the basic function behavior

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

@Injectable()
export class TestInterceptor 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`)))
      .pipe(map((data) => ({ data })));
  }
}
@Get(':id')
@UseInterceptors(TestInterceptor)
pipeTest(@Param('id', ParseIntPipe) id: any) {
  return id;
}

Decorator

  • A decorator is an expression which returns a function and can take a target, name and property descriptor as arguments.

  • It can be mainly used for return data in a easier way

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;

    return data ? user?.[data] : user;
  },
);
@Get()
async findOne(@User('firstName') firstName: string) {
  console.log(`Hello ${firstName}`);
}
  • It can be used to grouped different decorator together

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

export function Auth(...roles: Role[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized' }),
  );
}

Sequence

  • Incoming request

  • Globally bound middleware

  • Module bound middleware

  • Global guards

  • Controller guards

  • Route guards

  • Global interceptors (pre-controller)

  • Controller interceptors (pre-controller)

  • Route interceptors (pre-controller)

  • Global pipes

  • Controller pipes

  • Route pipes

  • Route parameter pipes

  • Controller (method handler)

  • Service (if exists)

  • Route interceptor (post-request)

  • Controller interceptor (post-request)

  • Global interceptor (post-request)

  • Exception filters (route, then controller, then global)

  • Server response

Unit Test

import { Injectable } from '@nestjs/common';
import { CatService } from 'src/cat/cat.service';
import { MouseService } from 'src/mouse/mouse.service';
import { PrismaService } from 'src/prisma/prisma.service';


@Injectable()
export class DogService {
  mouseService: MouseService;
  prismaService: PrismaService;
  catService: CatService;
  constructor(
    mouseService: MouseService,
    prismaService: PrismaService,
    catService: CatService,
  ) {
    this.mouseService = mouseService;
    this.prismaService = prismaService;
    this.catService = catService;
    console.log('init dog');
  }
  getCat(): string {
    return 'Cat';
  }
  getDog(): string {
    return 'dog';
  }
  getMouse(): string {
    return this.mouseService.getMouse();
  }
  async test(input: number) {
    return await this.mouseService.getNumber(input);
  }
  async prismaTest(input: number) {
    return await this.prismaService.user.delete({ where: { id: input } });
  }
}
import { Test } from '@nestjs/testing';
import { ModuleMocker, MockFunctionMetadata } from 'jest-mock';
import { DogService } from './dog.service';
import { mockDeep, DeepMockProxy, any } from 'jest-mock-extended';
import { MouseService } from 'src/mouse/mouse.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { User } from '@prisma/client';

const moduleMocker = new ModuleMocker(global);
let dogService: DogService;

describe('DogService', () => {
  let mouseService: DeepMockProxy<MouseService>;
  let prismaService: DeepMockProxy<PrismaService>;
  beforeEach(async () => {
    mouseService = mockDeep<MouseService>();
    prismaService = mockDeep<PrismaService>();
    const moduleRef = await Test.createTestingModule({
      providers: [
        DogService,
        { provide: MouseService, useValue: mouseService },
        { provide: PrismaService, useValue: prismaService },
      ],
    })
      .useMocker((token) => {
        if (typeof token === 'function') {
          const mockMetadata = moduleMocker.getMetadata(
            token,
          ) as MockFunctionMetadata<any, any>;
          const Mock = moduleMocker.generateFromMetadata(mockMetadata);
          return new Mock();
        }
      })
      .compile();

    dogService = moduleRef.get(DogService);
  });
  describe('test', () => {
    it('/test', () => {
      mouseService.getNumber.calledWith(1).mockResolvedValue(1);
      expect(dogService.test(1)).resolves.toBe(1);
    });
  });

  describe('prismaTest', () => {
    it('/test', () => {
      const deletedUser = { id: 1 };
      prismaService.user.delete
        .calledWith(any())
        .mockResolvedValue(deletedUser as User);
      expect(prismaService.user.delete({ where: { id: 1 } })).resolves.toEqual(
        deletedUser,
      );
      expect(dogService.prismaTest(1)).resolves.toBe(deletedUser);
    });
  });
});
  • The Test class is useful for providing an application execution context that essentially mocks the full Nest runtime, but gives you hooks that make it easy to manage class instances, including mocking and overriding.

  • Nest also allows you to define a mock factory to apply to all of your missing dependencies. This is useful for cases where you have a large number of dependencies in a class and mocking all of them will take a long time and a lot of setup. To make use of this feature, the createTestingModule() will need to be chained up with the useMocker() method, passing a factory for your dependency mocks.

  • Jest-mock-extended provides complete Typescript type safety for interfaces, argument types and return types. If your class has objects returns from methods that you would also like to mock, you can use mockDeep in replacement for mock.

We can use the library-specific (e.g., Express) , which can be injected using the @Res() decorator in the method handler signature (e.g., findAll(@Res() response)). With this approach, you have the ability to use the native response handling methods exposed by that object. For example, with Express, you can construct responses using code like response.status(200).send().

Middleware is a function which is called before the route handler. Middleware functions have access to the and objects, and the next() middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

Node.js
express
fastify
response object
request
response