使用 NestJS 開發 Node.js 應用

三毛丶發表於2018-12-02

NestJS 最早在 2017.1 月立項,2017.5 釋出第一個正式版本,它是一個基於 Express,使用 TypeScript 開發的後端框架。設計之初,主要用來解決開發 Node.js 應用時的架構問題,靈感來源於 Angular。在本文中,我將粗略介紹 NestJS 中的一些亮點。

元件容器

使用 NestJS 開發 Node.js 應用

NestJS 採用元件容器的方式,每個元件與其他元件解耦,當一個元件依賴於另一元件時,需要指定節點的依賴關係才能使用:

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

@Module({
  imports: [OtherModule],
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}
複製程式碼

依賴注入(DI)

與 Angular 相似,同是使用依賴注入的設計模式開發

使用 NestJS 開發 Node.js 應用

當使用某個物件時,DI 容器已經幫你建立,無需手動例項化,來達到解耦目的:

// 建立一個服務
@Inject()
export class TestService {
  public find() {
    return 'hello world';
  }
}

// 建立一個 controller
@Controller()
export class TestController {
  controller(
    private readonly testService: TestService
  ) {}
  
  @Get()
  public findInfo() {
    return this.testService.find()
  }
}
複製程式碼

為了能讓 TestController 使用 TestService 服務,只需要在建立 module 時,作為 provider 寫入即可:

@Module({
  controllers: [TestController],
  providers: [TestService],
})
export class TestModule {}
複製程式碼

當然,你可以把任意一個帶 @Inject() 的類,注入到 module 中,供此 module 的 Controller 或者 Service 使用。

背後的實現基於 Decorator + Reflect Metadata,詳情可以檢視 深入理解 TypeScript - Reflect Metadata

細粒化的 Middleware

在使用 Express 時,我們會使用各種各樣的中介軟體,譬如日誌服務、超時攔截,許可權驗證等。在 NestJS 中,Middleware 功能被劃分為 Middleware、Filters、Pipes、Grards、Interceptors。

例如使用 Filters,來捕獲處理應用中丟擲的錯誤:

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: any, 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,
      });
  }
}
複製程式碼

使用 interceptor,攔截 response 資料,使得返回資料格式是 { data: T } 的形式:

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

export interface Response<T> {
  data: T;
}

@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, Response<T>> {
  intercept(
    context: ExecutionContext,
    call$: Observable<T>,
  ): Observable<Response<T>> {
    return call$.pipe(map(data => ({ data })));
  }
}
複製程式碼

使用 Guards,當不具有 'admin' 角色時,返回 401:

import { ReflectMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => ReflectMetadata('roles', roles);

@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
複製程式碼

資料驗證

得益於 class-validatorclass-transformer 對傳入引數的驗證變的非常簡單:

// 建立 Dto
export class ContentDto {
  @IsString()
  text: string
}

@Controller()
export class TestController {
  controller(
    private readonly testService: TestService
  ) {}
  
  @Get()
  public findInfo(
    @Param() param: ContentDto     // 使用
  ) {
    return this.testService.find()
  }
}
複製程式碼

當所傳入引數 text 不是 string 時,會出現 400 的錯誤。

GraphQL

GraphQL 由 facebook 開發,被認為是革命性的 API 工具,因為它可以讓客戶端在請求中指定希望得到的資料,而不像傳統的 REST 那樣只能在後端預定義。

NestJS 對 Apollo server 進行了一層包裝,使得能在 NestJS 中更方便使用。

在 Express 中使用 Apollo server 時:

const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = express();
server.applyMiddleware({ app });

const port = 4000;

app.listen({ port }, () =>
  console.log(`Server ready at http://localhost:${port}${server.graphqlPath}`),
);
複製程式碼

在 NestJS 中使用它:

// test.graphql
type Query {
  hello: string;
}


// test.resolver.ts
@Resolver()
export class {
  @Query()
  public hello() {
    return 'Hello wolrd';
  }
}
複製程式碼

使用 Decorator 的方式,看起來也更 TypeScript

其他

除上述一些列舉外,NestJS 實現微服務開發、配合 TypeORM、以及 Prisma 等特點,在這裡就不展開了。

參考

相關文章