Binding Scopes

Global, Controller and Method Level

Guards, interceptors, pipes and filters can be bound at three levels. Global ones run first, then controller-level, then method-level (filters are the opposite: the most specific filter is tried first).

src/orders/orders.controller.ts

import { Body, Controller, Post, UseFilters, UseGuards, UseInterceptors, UsePipes, ValidationPipe } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { TimingInterceptor } from '../common/timing.interceptor';
import { PaymentErrorFilter } from './payment-error.filter';
import { CreateOrderDto } from './dto/create-order.dto';

@Controller('orders')
@UseGuards(JwtAuthGuard)                          // controller level: every route
@UseInterceptors(TimingInterceptor)
export class OrdersController {
  @Post()
  @UsePipes(new ValidationPipe({ whitelist: true })) // method level
  @UseFilters(PaymentErrorFilter)
  create(@Body() dto: CreateOrderDto) {
    return dto;
  }
}

Global Binding with DI

app.useGlobalGuards(new X()) in main.ts works, but the class is created outside the DI container, so it cannot inject services. Register globals as providers instead:

src/app.module.ts

import { Module } from '@nestjs/common';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
import { RolesGuard } from './auth/roles.guard';
import { TimingInterceptor } from './common/timing.interceptor';
import { AllExceptionsFilter } from './common/all-exceptions.filter';

@Module({
  providers: [
    { provide: APP_GUARD, useClass: JwtAuthGuard },     // order of APP_GUARD entries = run order
    { provide: APP_GUARD, useClass: RolesGuard },
    { provide: APP_INTERCEPTOR, useClass: TimingInterceptor },
    { provide: APP_PIPE, useValue: new ValidationPipe({ whitelist: true, transform: true }) },
    { provide: APP_FILTER, useClass: AllExceptionsFilter },
  ],
})
export class AppModule {}