📜 ⬆️ ⬇️

NestJS - the very real nodejs backend

image

NestJS is a framework designed to make a developer's life easier, using the right architectural approaches and dictating his own rules.

Therefore, NestJS is not only a backend framework, but also an opportunity to enter the world of advanced concepts, such as DDD , Event sourcing and microservice architecture. Everything is packaged in a simple and easy way, so the choice is yours - whether you decide to use the entire platform or just use its components.

To begin, tell you about my experience. For a long time I wrote on ASP.NET, then there was a frontend on AngularJS. In October 2016, there was a transition to Angular and Typescript. And here it is! Typing in the frontend, you can do complicated things easily enough! Prior to this (development on nestjs) at the node, I developed it solely for the sake of fun, and somehow there was even an attempt to bring good practices and typescript into the popular Koa . But NestJS is still a bit different.

NestJS, a framework that is completely written in TypeScript (it also supports JS, but the types are very good), it is easily tested and contains everything you need.

How to create a simple application on NestJS?

At NestJS under the hood turns express. Any extensions for express, easy to embed in Nest. But this is not the main thing here, with a strong desire, express can be taken and changed.

For starters, you can copy a small starter kit to yourself:

git clone https://github.com/nestjs/typescript-starter.git project 

The asynchronous function is enabled in server.ts, which is responsible for loading our application:

 import { NestFactory } from '@nestjs/core'; import { ApplicationModule } from './modules/app.module'; async function bootstrap() { const app = await NestFactory.create(ApplicationModule); await app.listen(3000); } bootstrap(); 

Well, then run npm run start and see the application on port 3000.

What does NestJS consist of?

The author of the framework was inspired by the ideas of Angular, and NestJS was very similar to Angular, especially in earlier versions.

Controllers

The controller layer is responsible for handling incoming requests and returning the response to the client. A simple example of a controller:

 import { Controller, Get } from '@nestjs/common'; @Controller('cats') export class CatsController { @Get() findAll() { return []; } } 

Providers

Almost everything is Providers - Service, Repository, Factory, Helper, etc. They can be embedded in controllers and other providers. If you say in Angular, then this is all `@Injectables

For example, the usual service:
 import { Injectable } from '@nestjs/common'; import { Cat } from './interfaces/cat.interface'; @Injectable() export class CatsService { private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); } findAll(): Cat[] { return this.cats; } } 

Modules

A module is a class with the Module () decorator. The Module () decorator provides metadata that Nest uses to organize the structure of the application. Each Nest application has at least one module, a root module. The root module is where Nest starts organizing the application tree. In fact, the root module may be the only module in your application, especially when the application is small, but it does not make sense. In most cases, you will have several modules, each of which has a closely related set of features. In Nest, the default modules are singletones, so you can easily share the same component instance between two or more modules.

 import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], components: [CatsService], }) export class CatsModule {} 

About dynamic modules

The modular system Nest comes with the function of dynamic modules. This allows you to create custom modules without any effort. Let's look at DatabaseModule:

 import { Module, DynamicModule } from '@nestjs/common'; import { createDatabaseProviders } from './database.providers'; import { Connection } from './connection.component'; @Module({ components: [Connection], }) export class DatabaseModule { static forRoot(entities = [], options?): DynamicModule { const providers = createDatabaseProviders(options, entities); return { module: DatabaseModule, components: providers, exports: providers, }; } } 

It defines the Connection component by default, but additionally, depending on the options and entities passed, it creates a collection of providers, for example, repository components. In fact, the dynamic module extends the module metadata. This essential feature is useful when you need to dynamically register components. Then you can import the DatabaseModule as follows:

 import { Module } from '@nestjs/common'; import { DatabaseModule } from './database/database.module'; import { User } from './users/entities/user.entity'; @Module({ imports: [ DatabaseModule.forRoot([User]), ], }) export class ApplicationModule {} 

By the way, to work with the database there is a cool TypeORM , able to work with most databases.

Middlewares

Middlewares is a function that is called before a route handler. They have access to request and response. In fact, they are the same as in express .

 import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common'; @Injectable() export class LoggerMiddleware implements NestMiddleware { resolve(...args: any[]): MiddlewareFunction { return (req, res, next) => { console.log('Request...'); next(); }; } } 

Exception Filters

Nest has an exception layer, which is responsible for intercepting unhandled exceptions and returning the corresponding response to the end user.

Each exception is handled by the global exception filter, and when it is not recognized (not by HttpException or a class that inherits from HttpException), the user receives the following JSON response:

 { "statusCode": 500, "message": "Internal server error" } 

Pipes

Pipe must implement the PipeTransform interface.
 import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common'; @Injectable() export class ValidationPipe implements PipeTransform { transform(value: any, metadata: ArgumentMetadata) { return value; } } 

Pipe converts the input to the desired result.

In addition, it can be passed for validation, since it is also possible for them to generate an exception if the data is incorrect. For example:

 @Post() @UsePipes(new ValidationPipe(createCatSchema)) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } 

Or you can declare a global pipe:

 async function bootstrap() { const app = await NestFactory.create(ApplicationModule); app.useGlobalPipes(new ValidationPipe()); await app.listen(3000); } bootstrap(); 

Guards

Guards must implement the CanActivate interface. Guards have the sole responsibility. They determine whether the request should be processed by the route handler or not.

 @Injectable() export class RolesGuard implements CanActivate { canActivate( context: ExecutionContext, ): boolean | Promise<boolean> | Observable<boolean> { // const request = context.switchToHttp().getRequest(); // const data = context.switchToWs().getData(); return true; } } Использование: <source lang="javascript"> @Controller('cats') @UseGuards(RolesGuard) export class CatsController {} 

Interceptors

Interceptors have a number of useful features that are inspired by Aspect-Oriented Programming (AOP) techniques. They allow:


Microservices

Nest Microservice is just an application that uses a different transport layer (not HTTP).

Nest supports two types of communication - TCP and Redis pub / sub, but the new transport strategy is easy to implement by implementing the CustomTransportStrategy interface.

You can easily create microservice from your application:

 import { NestFactory } from '@nestjs/core'; import { ApplicationModule } from './modules/app.module'; import { Transport } from '@nestjs/microservices'; async function bootstrap() { const app = await NestFactory.createMicroservice(ApplicationModule, { transport: Transport.TCP, }); app.listen(() => console.log('Microservice is listening')); } bootstrap(); 

Microservice Nest recognizes messages by templates. A pattern is a simple value, object, string, or even a number.

 import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; @Controller() export class MathController { @MessagePattern({ cmd: 'sum' }) sum(data: number[]): number { return (data || []).reduce((a, b) => a + b); } } 

And for communication between microservices, you must use the client:

 @Client({ transport: Transport.TCP, port: 5667 }) client: ClientProxy; 

And this is how sending a message will look like:

 @Get() call(): Observable<number> { const pattern = { cmd: 'sum' }; const data = [1, 2, 3, 4, 5]; return this.client.send<number>(pattern, data); } 

NestJS and Angular are so closely related that the author of the framework can be easily found at ng conferences and meetings. For example, recently the nrwl command included the nestjs template in its nx.

 ng g node-app nestjs-app -framework nestjs 

NestJS is already mature enough, and many companies are already using it.
Who uses NestJS now in production?

The framework itself: https://github.com/nestjs/nest

Many cool related links here: Awesome-nestjs
Russian-speaking community NestJS in the telegram https://t.me/nest_ru
Russian-language report about NestJS.

And of course subscribe to the channel in the telegram @ngFanatic where there is news about NestJS and Angular.

PS: This is only part of the NestJS features, about personal experience, a year long, there will be a separate article.

Source: https://habr.com/ru/post/439434/