background

In this article, I’ll build a CNode using Nest.js.

Why this article? I love NodeJs, even though MY NodeJs skills are mediocre. But I still use it to record my learning process.

Recently, I discovered the Nest.js framework, which effectively solves one of the challenges of the Nodejs project: architecture. Nest is designed to provide out-of-the-box applications that make it easy to create highly testable, scalable, loosely coupled and easy to maintain applications. Nest.js brings TypeScript to Node.js and wraps it around Express. So, I want to try writing a CNode using Nest.js. (PS: CNode is currently written in Egg.) I didn’t find a quick start on this topic, so I’ll give you my practices that you can easily extend to your projects.

The purpose of this article is not to introduce Nest.js. For those unfamiliar with Nest.js: It’s the framework for building Node.js Web applications. Although Node.js already contains many libraries for developing Web applications, none of them effectively address one of the most important topics: architecture.

Now, please fasten your seat belt, we are going to start.

What is the Nest

Nest is a powerful Node Web framework. It helps you easily build efficient, scalable applications. It uses modern JavaScript, is built in TypeScript, and combines the best concepts of OOP(object-oriented programming) and FP(functional programming).

It’s not just another framework. You don’t have to wait for a large community, because Nest is built with great, popular, well-known libraries — Express and socket.io! This means that you can quickly start using the framework without having to worry about third-party plug-ins.

Kamil Myśliwiec

JavaScript is awesome. Node.js gave us a possibility to use this language also on the server side. There are a lot of amazing libraries, helpers and tools on this platform, But non of them do not solve the main problem — the architecture. This is why I decided to create the Nest framework.

Important: Nest is inspired by Java Spring and Angular. It’s easy to learn if you’ve used Java Spring or Angular, which I’ve always used.

Core Concepts of Nest

The core concept of Nest is to provide an architecture that helps developers achieve maximum separation of layers and add abstraction to applications.

Architecture overview

Nest uses ES6 and ES7 features (decorators, async/await). If you want to use them, you need to use Babel or TypeScript to convert to ES5.

Nest uses TypeScript by default and can use JavaScript directly, but that doesn’t make sense.

If you’ve ever used Angular, this article will feel very familiar, as they are mostly written similarly. It doesn’t matter if you haven’t used them, I’ll take you through them.

Module, the Module

With Nest, you can naturally split your code into separate and reusable modules. The Nest Module is a class with an @Module() decorator. This decorator provides metadata that the framework uses to organize the application structure.

Each Nest app has a root module, usually named AppModule. The root module provides a boot mechanism to start the application. An application usually contains many functional modules.

Like JavaScript modules, @Modules can import functionality from other @Modules and allow them to export their own functionality for use by other @Modules. For example, to use nest’s Mongoose functionality in your app, you need to import MongooseModule.

Organizing your code into clear functional blocks helps manage the development of complex applications and enables reusability design. In addition, this technique allows you to use dynamic loading, which is used by MongooseModule.

The @Module decorator accepts an object whose properties describe the Module:

attribute describe
providers byNestServices instantiated by the injector can be shared between this module.
controllers Stores a set of created controllers.
imports Imports a list of modules for the providers required in this module.
exports Export this module for other modules to enjoyprovidersIn the service.

@Module declares a compilation context for a set of controllers focused on an application domain, a workflow, or a closely related set of capabilities. @Module can associate its controller with a set of related code, such as a service, to form a functional unit.

How to organize a module structure diagram

AppModule root module

  • CoreModule core modules (register middleware, filters, pipes, guards, interceptors, decorators, etc.)
  • SharedModule (registration service, mongodb, Redis, etc.)
  • ConfigModule Configuration module (System configuration)
  • FeatureModule (business modules, such as user modules, product modules, etc.)

In Nest, modules are singleton by default, so you can share the same instance of any provider across multiple modules. Shared modules are effortless.

The overall look is clean, and this is the module division I always use in Angular projects.

If you have better suggestions, welcome to communicate and improve with me.

Controller is the Controller

The controller is responsible for processing the request parameters passed in by the client and returning the response data to the client.

To create a basic Controller, we use the @Controller decorator. They associate classes with basic metadata, so Nest knows how to map controllers to the appropriate routes.

@Controller This is required to define the base Controller. @Controller(‘Router Prefix’) is the optional Prefix for every route registered in the class. Using a prefix avoids repeating itself when all routes share a common prefix.

@Controller('user') export class UserController { @Get() findAll() { return []; } @Get('/admin') admin() { return {}; }} // findAll = XXX /user/ /admin = XXX /user/admin

Controller is a relatively core function, all businesses are around it to develop. Nest also offers a number of related decorators. Here’s a brief description of each of them and how to use them later.

Request objects represent HTTP requests and have properties like request query strings, parameters, HTTP labels, and so on, but in most cases, you don’t need to retrieve them manually. We can use specialized decorators, such as @body () or @Query(), which are out of the box. Here’s how a decorator compares to a normal Express object.

First, the method parameter decorator:

Decorator name describe
@Request() The correspondingExpressthereqOr you can abbreviate it@req
@Response() The correspondingExpresstheresOr you can abbreviate it@res
@Next() The correspondingExpressthenext
@Session() The correspondingExpressthereq.session
@Param(param? : string) The correspondingExpressthereq.params
@Body(param? : string) The correspondingExpressthereq.body
@Query(param? : string) The correspondingExpressthereq.query
@Headers(param? : string) The correspondingExpressthereq.headers

First, method decorator:

Decorator name describe
@Post() The correspondingExpressthePostmethods
@Get() The correspondingExpresstheGetmethods
@Put() The correspondingExpressthePutmethods
@Delete() The correspondingExpresstheDeletemethods
@All() The correspondingExpresstheAllmethods
@Patch() The correspondingExpressthePatchmethods
@Options() The correspondingExpresstheOptionsmethods
@Head() The correspondingExpresstheHeadmethods
@Render() The correspondingExpresstheres.rendermethods
@Header() The correspondingExpresstheres.headermethods
@HttpCode() The correspondingExpresstheres.statusMethod, can cooperateHttpStatusThe enumeration

The above are basically controller decorators, and some common HTTP request parameters need to use the corresponding method decorators and parameters together.

Nest also offers two solutions for returning response data:

  1. When you return a JavaScript object or array directly, it is automatically parsed as JSON. When we return a string, Nest just sends a string without trying to parse it. By default, the status code for a response is always 200, except for POST requests, which use 201. You can easily change this behavior by using the @httpCode (httpStatus.xxxx) decorator.

  2. We can use the response object of kuttle, which we can inject into the function signature using the @res() modifier, Res.status (httpstatus.created).send() or res.status(httpstatus.ok).json([]).

Note: Do not use both methods at the same time. If both methods are used, the route will not work. If you find that the route does not respond, check whether the route is mixed. In normal cases, you are recommended to return the route in the first way.

Controllers must be registered with controllers in the module metadata to function properly.

Controller exception handling is explained later in filters.

Service and Dependency injection Provider Dependency Injection

A service is a broad concept that includes any values, functions, or features required by an application. A narrowly defined service is a class with a clearly defined purpose. It should do something concrete and do it well.

Nest separates controllers and services to improve modularity and reuse.

By separating your controller’s logic-related functions from other types of processing, you can make your controller classes leaner and more efficient. Ideally, the controller’s job is to declare the decorator and response data and nothing else. It should provide a request and response bridge to act as an intermediary between the view (rendered by the template) and the application logic (usually containing some concept of the model).

The controller does not need to define anything like fetching data from the client, validating user input, or writing logs directly to the console. Instead, delegate these tasks to services. By defining various processing tasks into injectable service classes, you can make it available to any controller. You can also make your application more adaptable by injecting different providers of the same service in different environments.

Nest does not enforce these principles. It simply makes it easier to decompose application logic into services and make those services available to individual controllers through dependency injection.

A controller is a consumer of a service, that is, you can inject a service into the controller so that the controller class can access the service class.

Then a service is a provider, and basically, almost anything can be thought of as a provider – a service, repository, factory, helper, and so on. They can both inject dependencies through constructors, which means they can create various relationships with each other.

To define a class as a service in Nest, the @Injectable decorator provides metadata that Nest can inject into the controller as a dependency.

Also, use the @Injectable decorator to indicate that a controller or other class (another service, module, etc.) has a dependency. A dependency doesn’t have to be a service; it can be a function or a value, and so on.

Dependency injection (DI) was introduced into the Nest framework and used everywhere to provide services or other things needed for new controllers.

The injector is the primary mechanism. You don’t have to create your own Nest injector. Nest creates an app-wide injector for you during startup.

The injector maintains a container containing instances of dependencies it has created and reuses them whenever possible.

A provider is a recipe for creating a dependency. In the case of a service, it is usually the service class itself. Any class you want to use in your application must register a provider with the application’s injector so that the injector can use it to create new instances.

Angular is probably the best known front-end framework for dependency injection, as described here.

Import {Injectable} from '@nestjs/common'; interface User {} @Injectable() export class UserService { private readonly user: User[] = []; create(cat: User) { this.user.push(User); } findAll(): User[] { return this.user; } // user Controller import {Controller, Get, Post, Body} from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Post() async create(@Body() createUserDto: CreateUserDto) { this.userService.create(createUserDto); } @Get() async findAll(): Promise<User[]> { return this.userService.findAll(); }}

Custom service

We can define services not only with @Injectable(), but also in three other ways: value, Class, and Factory. This, like Angular, defaults to @Injectable() to define a service as a class.

The value of use:

const customObject = {};
@Module({
    controllers: [ UsersController ],
    components: [
        { provide: UsersService, useValue: customObject }
    ],
})

Note: useValue can be any value. In this module, Nest will associate the customObject with the UsersService. You can also use the test surrogate (unit test).

Use the class:

import { UserService } from './user.service';
const customObject = {};
@Module({
    controllers: [ UsersController ],
    components: [
        { provide: UsersService, useClass: UserService }
        OR
        UserService
    ],
})

Note: Just use the selected, more specific classes in this module. UseClass can be the same as provide, or useClass can replace provide if it is different. Simple understanding of method change, do not change method name, commonly used to deal with dependency injection in different environments.

Use the factory:

@Module({
    controllers: [ UsersController ],
    components: [
        ChatService,
        {
            provide: UsersService,
            useFactory: (chatService) => {
                return Observable.of('customValue');
            },
            inject: [ ChatService ]
        }
    ],
})

Note: You want to provide a value that must be computed using other components (or custom package features) and you want to provide asynchronous values that return only observable or promised values, such as database connections. Inject depends on the service, provide the registration name, useFactory processing mode, useFactory parameter and inject array in the same order.

What if we provide the registry name is not a service, but a string key, which is also commonly used.

@Module({
    controllers: [ UsersController ],
    components: [
        { provide: 'isProductionMode', useValue: false }
    ],
})

To use the selected custom string key, you must tell Nest to use the @inject () decorator, like this:

import { Component, Inject } from 'nest.js';

@Component()
class SampleComponent {
    constructor(@Inject('isProductionMode') private isProductionMode: boolean) {
        console.log(isProductionMode); // false
    }
}

There is also a loop-dependent pit, which will be explained how to avoid and solve later.

A service must register with the module’s metadata providers to work properly. If you want to use it for other modules, you need to add it to Exports.

Middleware Middleware

Middleware is a function called before the routing handler. Middleware functionality can access request and response objects, as well as the next middleware functionality in the application request-response cycle. The next middleware function is usually represented by a variable named next. Middleware in Express is very well known.

By default, Nest middleware is equivalent to Express middleware. Similar to Express middleware functionality, the middleware functionality performs the following tasks

  • Execute any code.
  • Make changes to the request and response objects.
  • The request-response period ends.
  • Call the next middleware function in the stack.
  • If the current middleware function does not end the request-response cycle, it must be callednext()Pass control to the next middleware function. Otherwise, the request will be suspended.

The simple understanding of Nest middleware is that the Express middleware is wrapped. So the advantage is that if you want to use middleware, you can search Express middleware right away and use it. Is it convenient?

Nest middleware is either a function or a class with the @Injectable() decorator. Classes should implement the NestMiddleware interface, while functions have no special requirements.

Import {Injectable, NestMiddleware, MiddlewareFunction} from '@nestjs/common'; 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(); }; }}

There are two ways to use it:

  1. Middleware can be registered globally
Async function bootstrap() {// Create nest.js instance const app = await nestfactory. create(AppModule, Application, {bodyParser: true, }); // Register middleware app.use(LoggerMiddleware()); // listen to port 3000 await app.listen(3000); } bootstrap();
  1. Middleware can be locally registered in modules
export class CnodeModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .with('ApplicationModule') .exclude( { path: 'user', method: RequestMethod.GET }, { path: 'user', method: RequestMethod.POST }, ) .forRoutes(UserController); } } // or export class CnodeModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes('*'); }} // 1. Resolve, exclude, forRoutes, // 2

Note: they are registered in different places and affect different routes. Global registration affects all routes, while local registration only affects routes under the current route.

Filter Exception filter

The exception filter layer is responsible for handling all thrown exceptions throughout the application. When an unhandled exception is found, the end user receives an appropriate user-friendly response.

By default, the response JSON information is displayed

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

Use the underlying filter

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}

HttpException takes two arguments:

  • Message content, which can be a string error message or an object{status: status code, error: error message}
  • Status code

It’s cumbersome to write so many at a time, so the filter also supports extension and customization of shortcut filter objects.

export class ForbiddenException extends HttpException { constructor() { super('Forbidden', HttpStatus.FORBIDDEN); }}

You can use it directly:

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException('Forbidden');
}

Isn’t it? It’s a lot easier.

Nest gives us a number of quick and common HTTP status errors like this:

  • BadRequestException 400
  • UnauthorizedException 401
  • ForbiddenException 403
  • NotFoundException 404
  • NotAcceptableException 406
  • RequestTimeoutException 408
  • ConflictException 409
  • GoneException 410
  • PayloadTooLargeException 413
  • UnsupportedMediaTypeException 415
  • UnprocessableEntityException 422
  • InternalServerErrorException 500
  • NotImplementedException 501
  • BadGatewayException 502
  • ServiceUnavailableException 503
  • GatewayTimeoutException 504

Exception handlers are pretty basic, but sometimes you might want to completely control the exception layer, for example, by adding some logging or using a different JSON schema based on a few selected factors. As mentioned above, Nest gives us built-in return response template, which is not acceptable, we need to customize how to do, Nest gives us expansion space.

import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common'; import { HttpException } from '@nestjs/common'; @Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus(); response .status(status) .json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, }); }}

It returns an Express method response to customize its own response exception format.

There are four ways to use it:

  1. directly@UseFilters()Used in the decorator for the response result of the current route
@Post()
@UseFilters(HttpExceptionFilter | new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException();
}
  1. directly@UseFilters()Decorator, used in the current controller to route all response results
@UseFilters(HttpExceptionFilter | new HttpExceptionFilter())
export class CatsController {}
  1. Use the built-in instance method in global registrationuseGlobalFiltersThe whole project. Filters this comparison generally recommends global registration.
async function bootstrap() {
  const app = await NestFactory.create(ApplicationModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

Pipeline Pipe

Pipes can take your request parameters and validate type, object structure, or mapping data against specific criteria. A pipe is a pure function and should not select or invoke any service operations from the database.

Define a simple pipe:

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

Pipes are classes annotated with the @Injectable() decorator. The PipeTransform interface should be implemented in the Transform implementation, much like Angular.

Nest handles request data validation and can throw an exception if the data is incorrect, using filters to catch it.

Nest has built in two generic pipes for us, a data ValidationPipe and a data conversion pipe.

Using the ValidationPipe requires class-Validator class-Transformer, and if you do not have them installed, you will get an error using the ValidationPipe.

Tip: ValidationPipe not only validates request data, but also casts it.

How do you use it? There are four ways

  1. directly@Body()Used in the decorator, only the current body argument is used
// user Controller import {Controller, Get, Post, Body} from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Post() async create(@Body(ValidationPipe | new ValidationPipe()) createUserDto: CreateUserDto) { this.userService.create(createUserDto); }}
  1. in@UsePipes()Decorator for all request parameters of the current route
// user Controller import {Controller, Get, Post, Body} from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Post() @UsePipes(ValidationPipe | new ValidationPipe()) async create(@Body() createUserDto: CreateUserDto) { this.userService.create(createUserDto); }}
  1. in@UsePipes()Decorator used in the current controller to route all request parameters
// user Controller import {Controller, Get, Post, Body} from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') @UsePipes(ValidationPipe | new ValidationPipe()) export class UserController { constructor(private readonly userService: UserService) {} @Post() async create(@Body() createUserDto: CreateUserDto) { this.userService.create(createUserDto); }}
  1. Use the built-in instance method in global registrationuseGlobalPipesThe whole project. This pipe is generally recommended for global registration.
async function bootstrap() {
  const app = await NestFactory.create(ApplicationModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

So how to play createUserDto will be explained in this tutorial, but not here.

@Get(':id')
async findOne(@Param('id', ParseIntPipe | new ParseIntPipe()) id) {
  return await this.catsService.findOne(id);
}

ParseIntPipe is also very simple to use, converting a string to a number. Get, put, patch, delete, etc. This is especially useful when you have an ID. You can also do pagination processing, which is used in the actual combat.

Guard Guard

The guard can do permission authentication, if you do not have permission to deny you access to the route, default return 403 error.

Define a simple pipe:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class AuthGuard implements CanActivate { canActivate( context: ExecutionContext, ): boolean | Promise<boolean> | Observable<boolean> { const request = context.switchToHttp().getRequest(); return validateRequest(request); }}

Guards are classes annotated with the @Injectable() decorator. The CanActivate interface should be implemented. This code is implemented in the CanActivate method and returns a Boolean value (true) indicating permission, false raising 403. This is similar to Angular.

How do you use it? There are two ways

  1. directly@UseGuards()Decorator used in the current controller to route all request parameters
@Controller('cats')
@UseGuards(RolesGuard | new RolesGuard())
export class CatsController {}
  1. Use the built-in instance method in global registrationuseGlobalGuardsThe whole project.
const app = await NestFactory.create(ApplicationModule);
app.useGlobalGuards(new RolesGuard());

If you don’t do authentication related to permission management, you won’t need this feature. But it’s still useful for abstraction. We’re going to use that in this field project as well.

The Interceptor Interceptor

Interceptors are a particularly powerful feature, similar to AOP in aspect oriented programming, but also used in front-end programming, such as various HTTP request libraries that provide similar functionality. Well-known framework Angular framework HTTP module. Famous libraries include the old jquery and the new axios.

Define a simple interceptor:

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

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    call$: Observable<any>,
  ): Observable<any> {
    console.log('Before...');
    const now = Date.now();
    return call$.pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}

Interceptors are classes annotated with the @Injectable() decorator. The NestInterceptor interface should be implemented in the Intercept method, which returns an Observable, much like Angular.

What interceptors can do:

  • Bind additional logic before/after method execution
  • Converts the result returned from the function
  • Converts an exception thrown from a function
  • Extend the basic function behavior
  • Full coverage of a function depends on the conditions selected (such as caching)

How do you use it? Three ways

  1. directly@UseInterceptors()Decorator, used in the current route, can also pass parameters, need special treatment, written as a higher-order function, can also use dependency injection.
@Post('upload')
@UseInterceptors(FileFieldsInterceptor | FileFieldsInterceptor([
  { name: 'avatar', maxCount: 1 },
  { name: 'background', maxCount: 1 },
]))
uploadFile(@UploadedFiles() files) {
  console.log(files);
}
  1. directly@UseInterceptors()Used in decorator for the current controller route, this cannot pass parameters, can use dependency injection
@UseInterceptors(LoggingInterceptor | new LoggingInterceptor())
export class CatsController {}
  1. Use the built-in instance method in global registrationuseGlobalInterceptorsThe whole project.
const app = await NestFactory.create(ApplicationModule);
app.useGlobalInterceptors(new LoggingInterceptor());

Interceptors can do many things, such as caching, response data conversion, exception capture conversion, response timeout error, and print request response logs. We’re going to use that in this field project as well.

conclusion

Modules are basic units divided according to business logic, including controllers and services. The controller is the part that handles the request and response data, and the service is the part that handles the actual business logic.

Middleware is the data processing layer before the routing processing Handler, which can only be registered in the module or globally and can be used for log processing middleware, user authentication middleware and other processing. The middleware is the same as the middleware of Express, so it can access the whole context of request and response. The module scope can rely on injection service. Global registration can only be a pure function or a higher-order function.

Pipeline is data flow processing, before the middleware back-end processing data processing, can be used in the controller class, method, method parameters, global registration, can only be a pure function. Can do data validation, data conversion and other data processing.

Guard is to determine whether the request can reach the corresponding route processor, can know the execution context of the current route, can be used in the controller class, method, global registration, can do role guard.

Interceptors are used to process the relevant logic before and after entering the controller, know the execution context of the current route, can be used in the controller class, method, global registration, can do logging, transaction processing, exception handling, response data format and so on.

The filter catches the error message and returns the response to the client. Can be used in the controller class, method, global registration, can do custom response exception format.

Middleware, filters, pipes, guards, interceptors, these are just a few of the confusing things. What they all have in common is an intermediate abstraction layer that hooks into the controller, but their responsibilities are different.

Global pipes, guards, filters, and interceptors are loosely coupled to any module. They cannot rely on injecting any service because they do not belong to any module. You can use controller scopes, method scopes, or auxiliary scopes that are supported only by pipes, except for middleware that is module scopes, which are both controller scopes and method scopes.

Note that global pipes, guards, filters, and interceptors can only be new. Global middleware is a pure function. Middleware module registration also cannot use new, can be dependency injection. Local registrations for pipes, guards, filters, and interceptors can use new and class names, and all but pipes can be dependency injected. Interceptors and guards can be written as higher-order methods to pass parameters for custom purposes.

Pipes, filters, interceptor guards all have specific responsibilities. Interceptors and guards are integrated with the module, while pipes and filters run outside the module area. The pipeline task is to validate type, object structure, or mapping data against specific criteria. The filter task is to catch various errors back to the client. The pipe is not the proper place to select or invoke any services from the database. Interceptors, on the other hand, should not validate object schemas or modify data. If overwriting is required, it must be caused by the database calling the service. The guard determines which routes are accessible, and it takes over your authentication responsibility.

Then you must be most concerned about the order in which they execute:

Client request -- - > middleware -- -- -- -- -- -- > > guard blocker before -- -- -- -- - > > pipeline controller processing and response - > - > after the interceptor filterCopy the code

Let’s look at two pictures,

Request return response result:

Abnormal response:

Hello World

Learning a language and a technology starts from Hello World, and we also start our Nest journey from zero to Hello World

Prepare the required development environment and tools

NVM is recommended to manage nodeJS versions. Download the version for your computer.

  1. Preparation environment: Nodejs V8 + (current version v10+, must be at least 8, high support for ES2015)
  2. Prepare database: mongodb V3 + (current version V4 +)
  3. Prepare database: Redis V3 + (current version V3 +)
  4. Ready editor: VS Code latest version (native Windows V1.26)
  5. vs codeRecommended plug-ins :(other plug-ins are optional)
    • Debugger for Chrome
    • Ejs — EJS file highlighting
    • Beautify — Code formatting
    • DotENV –. Env is highlighted
    • Jest — Nest default test framework support
    • TSLint — TS syntax checking
    • TypeScript Hero — TS prompts
    • vscode-icons — icons
  6. Recommend a few useful tools:
    • Postmen — API testing magic
    • Robomongo — mongodb graphical tool
    • Redis Desktop Manager – Redis graphics tools
    • Cmder – Windows command line artifact

Nest Related Resources

  1. Website: nestjs.com
  2. Documents: docs.nestjs.com
  3. English: docs.nestjs.cn
  4. Github:github.com/nestjs/nest
  5. Version: Stable version V5.1.0
  6. CLI:github.com/nestjs/nest…

nest-cli

Nest – CLI is a Nest project scaffolding. Provide us with an initialization module that allows us to quickly complete the Hello World function.

The installation

npm i -g @nestjs/cli

Common commands:

New (short: n) Builds new projects
$ nest new my-awesome-app
OR
$ nest n my-awesome-app
Generate (G) Generate a file
  • Class – CL class
  • Controller – CO Controller
  • Decorator – D) decorator
  • Exception – E) Exception catching
  • Filter (f) Filter
  • Gateway (GA) Gateway
  • Guard (gu) A guard
  • Interceptor (I) Interceptor
  • Middleware – MI middleware
  • Module – MO module
  • Pipe – PI pipe
  • Provider (PR) provider
  • Service – S Service

Create a Users service file

$ nest generate service users
OR
$ nest g s users

Note:

  1. Must beIn the projectThe root directorySRC/by default. (Cannot be created in the current folder, otherwise XXX/SRC/XXX will be generated automatically. This is not as smart as Angular-CLI.
  2. Need to beGive priority toCreate a new module, otherwise services, controllers, etc. created outside the module will be automatically injected and updated into the parent module
Info (I) Prints the version information

Print the current system, use nest core module version, for you to officially submit issues

| \ | | | | | _ | / ___ | __ \ | | | _ _ | | \ | | ___ ___ | | _ | | \ ` -. | / \ | | | | |. ` | / _ \ / __ | | __ | | | ` -. \ | | | | | | | | \ | | / \ (\ | | _ / \ __ / / / / __ / / | / __ / \ | | _____ | | _ \ _ | \ _ / \ ___ | | ___ payable / \ | \ _____ there comes / \ \ _____ / \ \ / ___ / _____ [System Information] OS Version: Windows 10 NodeJS Version: v8.11.1 NPM Version: 5.6.0 [Nest Information] Microservices Version: 5.1.0 WebSockets Version: 5.1.0 Testing version: 5.1.0 Common Version: 5.1.0 Core Version: 5.1.0Copy the code

Finally, the overall functionality is similar to angular-CLI in that it is simple and useful. Build projects, generate files, and print version information.

Built-in Features of Nest

Currently, Nex.js supports Express and Fastify. I am not familiar with Fastify, so I choose Express for this article.

The core module

  • @nestjs/common provides many decorators, log services, etc
  • @nestjs/core Core module handles underlying framework compatibility
  • @nestjs/ Microservices Microservices support
  • @nestjs/testing
  • @ nestjs/web sockets websocket support

Optional modules

  • @nestjs/ Typeorm not played yet
  • @nestjs/ graphQL not played yet
  • @nestjs/ CQRS haven’t played yet
  • @nestjs/ Passport authentication (version V5 support, not backward compatible)
  • @nestjs/swagger swagger UI API
  • @ nestjs/mongoose mongoose module

Note: Other middleware modules, as long as they support Express and can be used.

Build the project

  1. Create a projectnest-cnode
nest new nest-cnode

Submit your description, initialization version, author, and a package manager to choose node_modules installation mode NPM or YARN.

  1. Project start
CD nest-cnode // Start command NPM run start // Preview NPM run start:dev // Develop NPM run prestart:prod // Compile into JS NPM run start:prod // Production // test command NPM run test // unit test NPM run test: COV // Unit test + coverage generated NPM run test: e2E // E2E test
  1. Introduction to project Files
file instructions
node_modules NPM package
src The source code
logs The log
test E2E test
nodemon.json Nodemon Configuration (NPM run start:dev starts)
package.json NPM package management
README.md Description file
tsconfig.json Typescript configuration files (Typescript required)
tslint.json Typescript style checking files (Typescript mandatory)
webpack.config.js Hot update (NPM run start: HMR start)
.env The configuration file

The development code is in SRC, and the generated code is in dist (packaged and compiled automatically)

We open a browser, go to http://localhost:3000, and you should see a page that displays the text Hello World.

That’s where we left off. Look at our next project, Nest-CNode