NestJS: Enterprise-grade Node Framework

NestJS architecture with modules, controllers and providers, dependency injection, DTO validation with class-validator, exception filters, and when to choose it over Express.

IntermediateUpdated 2026-10-2847K views

Why NestJS

Express is flexible but leaves architecture to you. NestJS (TypeScript-first, heavily inspired by Angular) gives a fixed project layout: modules, controllers, providers, pipes, guards and interceptors — plus a DI container, testing utilities and official integrations (TypeORM, Prisma, GraphQL, microservices, WebSockets).

Modules, Controllers, Providers

// users.controller.ts
@Controller('users')
export class UsersController {
  constructor(private readonly users: UsersService) {}  // 构造注入

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.users.findOne(id);
  }

  @Post()
  create(@Body() dto: CreateUserDto) {
    return this.users.create(dto);
  }
}

// users.module.ts
@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

// app.module.ts 汇总
@Module({ imports: [UsersModule, DbModule] })
export class AppModule {}

DTO Validation and Pipes

import { IsEmail, IsInt, Min } from 'class-validator';

export class CreateUserDto {
  @IsEmail() email: string;
  @Min(2) name: string;
  @IsInt() age?: number;
}

// 全局开启 ValidationPipe(main.ts)
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,        // 剔除 DTO 上不存在的字段
    transform: true,        // 自动转类型
  }),
);

// 全局异常过滤器:统一错误响应
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const res = host.switchToHttp().getResponse();
    res.status(500).json({ code: 500, message: 'server error' });
  }
}

Cross-Cutting Concerns: Guards and Interceptors

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private jwt: JwtService) {}
  async canActivate(ctx: ExecutionContext): Promise<boolean> {
    const req = ctx.switchToHttp().getRequest();
    const token = req.headers.authorization?.replace('Bearer ', '');
    try { req.user = await this.jwt.verifyAsync(token); return true; }
    catch { return false; }
  }
}

// 拦截器:统一响应格式 / 缓存 / 记录耗时
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(ctx: ExecutionContext, next: CallHandler) {
    console.log('before', ctx.getHandler().name);
    return next.handle().pipe(tap(() => console.log('after')));
  }
}
Choose Express for small APIs and full control; choose NestJS for teams and long-lived projects where convention beats configuration.

Friends & Partners