メインコンテンツまでスキップ

Nest Quick Note

· 約5分

Controller

  • HTTP Requestを処理するやつ

  • @Controller()をクラス定義前に付ける

    @Controller('cats')
    export class CatsController {
  • 上記の例は/catsがパスになる

  • GETメソッドの定義

    @Get()
    findAll(): string {
    return 'This action returns all cats';
    }
  • @Get('breed')だとパスは/cats/breedになる

Provider

  • Service, Respository, Factory, Helperなどの総称

  • ProviderModuleに登録すると使える

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

Service

  • DBなどからデータをとってくるやつ

  • @Injectable()をクラス定義に付ける

    @Injectable()
    export class CatsService {
  • Controllerのコンストラクタの引数にすればインスタンスを注入できる

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

Module

  • アプリを構造化するやつ

  • @Moduleをクラス定義につける

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

MiddleWare

  • HTTPリクエストの処理の前に付けるやつ

  • 関数でもクラスでも作成できる

  • クラスの例

    @Injectable()
    export class LoggerMiddleware implements NestMiddleware {
    use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
    }
    }
  • Moduleconfigureメソッドで注入できる

    @Module({
    imports: [CatsModule],
    })
    export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
    consumer
    .apply(LoggerMiddleware) // Middlewareを登録
    .forRoutes('cats'); // `/cats`パスにMiddlewareを適用
    }
    }
  • forRoutesControllerを渡してもよい

    .forRoutes(CatsController);
  • Fuctional middlewareの例

    次の引数も持ったただの関数

    export function logger(req: Request, res: Response, next: NextFunction) {
    console.log(`Request...`);
    next();
    };
  • 以下のように適用する

    consumer
    .apply(logger)
    .forRoutes(CatsController);

Pipe

  • Route handlerの引数に適用して、引数の変換やバリデーションを行うやつ

  • ビルトインのParseIntPipeの使用例

    @Get(':id')
    async findOne(@Param('id', ParseIntPipe) id: number) {
    return this.catsService.findOne(id);
    }
  • インスタンス化を自分ですることもできる

    @Get(':id')
    async findOne(
    @Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
    id: number,
    ) {
    return this.catsService.findOne(id);
    }
  • Pipeの処理が失敗すると例外を投げる

  • Custom pipeの例

    @Injectable()
    export class ValidationPipe<T, R> implements PipeTransform<T, R> {
    transform(value: T, metadata: ArgumentMetadata): R { // 必須メソッド
    return convert(value);
    }
    }
  • valuePipeを適用する関数の引数

  • ArgumentMetadataは以下の情報を持っている

    export interface ArgumentMetadata {
    type: 'body' | 'query' | 'param' | 'custom';
    metatype?: Type<unknown>;
    data?: string;
    }

Guard

  • Route handlerの前に作用して、そのRoute handlerを実行するかしないかを決定するやつ

  • Middlewareに比べて、どのRoute handlerに適用されるのか詳細に設定できる

  • よく認証の確認に使われる

    @Injectable()
    export class AuthGuard implements CanActivate {
    // 必須のメソッド
    canActivate(
    context: ExecutionContext,
    ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
    }
    }
  • Guardの適用

    @Controller('cats')
    @UseGuards(RolesGuard)
    export class CatsController {}
  • @UseGuardsController内のメソッドにも適用できる

  • Guard内で対象のRoute handlerに付けられたdecoratorを参照することが可能

    Route handlerに付けるCustom decoratorの作成:

    export const Roles = Reflector.createDecorator<string[]>();

    RolesRoute handlerに適用する:

    @Post()
    @Roles(['admin'])
    async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
    }

    RolesGuardから参照する:

    @Injectable()
    export class RolesGuard implements CanActivate {
    constructor(private reflector: Reflector) {}

    canActivate(context: ExecutionContext): boolean {
    // ExecutionContextからRolesを取得できる
    const roles = this.reflector.get(Roles, context.getHandler());
    if (!roles) {
    return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return matchRoles(roles, user.roles);
    }
    }

Interceptor

  • Route handlerの前後に処理を挟むことができるやつ

  • NestInterceptorを継承する

  • intercept()メソッドを実装する

  • intercept()メソッドの引数はExecutionContextCallHandlerの2つ

  • CallHandlerRoute handlerのこと

  • intercept()メソッドの中でCallHandlerを実行しないと、Route handlerは実行されない

  • LoggingInterceptorの例

    @Injectable()
    export class LoggingInterceptor implements NestInterceptor<T, R> {
    intercept(context: ExecutionContext, next: CallHandler): Observable<R> {
    console.log('Before...');

    const now = Date.now();
    return next
    .handle() // Observale<T>
    .pipe(
    tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
    }
    }
  • Interceptorの適用

    @UseInterceptors(LoggingInterceptor)
    export class CatsController {}
  • メソッドレベルにも適用できる

参考