从零入门 NestJS:构建你的第一个 REST API(三)项目结构解析

0 阅读3分钟

项目结构解析

NestJS 项目初始化后,src/ 目录下的结构清晰、职责分明。理解这些文件的作用,是高效开发的第一步。

典型目录结构

my-nest-app/
  src/
    app.controller.ts      // 应用主控制器
    app.controller.spec.ts // 控制器单元测试
    app.module.ts          // 应用主模块
    app.service.ts         // 应用主服务
    main.ts                // 应用入口文件
    user/                  // 用户业务模块
    order/                 // 订单业务模块
    share/                 // 共享代码(工具、常量、DTO等)
    filters/               // 全局/自定义异常过滤器
    decorators/            // 自定义装饰器
    interceptors/          // 拦截器(如日志、响应格式化等)
    guards/                // 守卫(如权限、认证)
    pipes/                 // 管道(如参数校验、转换)
    middlewares/           // 中间件(如日志、请求追踪)

关键文件说明

  • main.ts

    • 应用程序入口,负责启动 Nest 应用。
    • 常见操作:端口监听、中间件挂载、全局拦截器/管道注册等。
    // main.ts
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { LoggingInterceptor } from './interceptors/logging.interceptor';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      app.useGlobalInterceptors(new LoggingInterceptor()); // 全局拦截器
      await app.listen(3000);
      console.log('应用已启动:http://localhost:3000');
    }
    bootstrap();
    
  • app.module.ts

    • 应用的根模块,负责组织和导入其他功能模块。
    • 通过 @Module 装饰器声明 controllers、providers、imports 等。
    // app.module.ts
    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { UserModule } from './user/user.module';
    import { OrderModule } from './order/order.module';
    import { APP_FILTER } from '@nestjs/core';
    import { AllExceptionsFilter } from './filters/all-exceptions.filter';
    
    @Module({
      imports: [UserModule, OrderModule],
      controllers: [AppController],
      providers: [
        AppService,
        { provide: APP_FILTER, useClass: AllExceptionsFilter }, // 全局异常过滤器
      ],
    })
    export class AppModule {}
    
  • app.controller.ts

    • 控制器,负责处理路由请求和响应。
    • 通过 @Controller 装饰器定义路由前缀,方法上用 @Get@Post 等装饰器定义接口。
    // app.controller.ts
    import { Controller, Get } from '@nestjs/common';
    import { AppService } from './app.service';
    
    @Controller()
    export class AppController {
      constructor(private readonly appService: AppService) {}
    
      @Get()
      getHello(): string {
        return this.appService.getHello();
      }
    }
    
  • app.service.ts

    • 服务层,负责业务逻辑处理。
    • 通过 @Injectable 装饰器声明,可被控制器等依赖注入。
    // app.service.ts
    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class AppService {
      getHello(): string {
        return 'Hello World!';
      }
    }
    
  • share/

    • 存放项目中可复用的工具函数、常量、DTO(数据传输对象)、通用类型等。
    • 例如:
    // share/constants.ts
    export const JWT_SECRET = 'my_jwt_secret';
    
    // share/utils.ts
    export function toResponse(data: any) {
      return { code: 0, data };
    }
    
  • filters/

    • 存放全局或自定义的异常过滤器,统一处理错误响应。
    • 例如:
    // filters/all-exceptions.filter.ts
    import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
    import { Response } from 'express';
    
    @Catch()
    export class AllExceptionsFilter implements ExceptionFilter {
      catch(exception: unknown, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse<Response>();
        const status = exception instanceof HttpException ? exception.getStatus() : 500;
        response.status(status).json({
          code: status,
          message: exception instanceof HttpException ? exception.message : '服务器错误',
        });
      }
    }
    
  • decorators/

    • 存放自定义装饰器,简化控制器或服务的参数注入、权限校验等。
    • 例如:
    // decorators/user.decorator.ts
    import { createParamDecorator, ExecutionContext } from '@nestjs/common';
    
    export const CurrentUser = createParamDecorator(
      (data: unknown, ctx: ExecutionContext) => {
        const request = ctx.switchToHttp().getRequest();
        return request.user;
      },
    );
    
  • interceptors/

    • 存放拦截器,用于统一处理请求/响应、日志、数据格式化等。
    • 例如:
    // interceptors/logging.interceptor.ts
    import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
    import { Observable, tap } from 'rxjs';
    
    @Injectable()
    export class LoggingInterceptor implements NestInterceptor {
      intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        const now = Date.now();
        return next.handle().pipe(
          tap(() => console.log(`请求耗时: ${Date.now() - now}ms`)),
        );
      }
    }
    
  • guards/

    • 存放守卫,用于权限控制、认证等。
    • 例如:
    // guards/auth.guard.ts
    import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
    
    @Injectable()
    export class AuthGuard implements CanActivate {
      canActivate(context: ExecutionContext): boolean {
        const request = context.switchToHttp().getRequest();
        return Boolean(request.headers['authorization']); // 简单示例
      }
    }
    
  • pipes/

    • 存放管道,用于参数校验、数据转换等。
    • 例如:
    // pipes/parse-int.pipe.ts
    import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
    
    @Injectable()
    export class ParseIntPipe implements PipeTransform<string, number> {
      transform(value: string): number {
        const val = parseInt(value, 10);
        if (isNaN(val)) {
          throw new BadRequestException('参数必须为数字');
        }
        return val;
      }
    }
    
  • middlewares/

    • 存放中间件,用于日志、请求追踪、跨域等。
    • 例如:
    // middlewares/logger.middleware.ts
    import { Injectable, NestMiddleware } from '@nestjs/common';
    import { Request, Response, NextFunction } from 'express';
    
    @Injectable()
    export class LoggerMiddleware implements NestMiddleware {
      use(req: Request, res: Response, next: NextFunction) {
        console.log(`[${req.method}] ${req.originalUrl}`);
        next();
      }
    }
    

实战场景:快速定位与扩展

假如你要新增一个用户模块,只需在 src/ 下新建 user/ 目录,并在 app.module.tsimports 中注册即可。清晰的结构让多人协作和后期维护变得非常高效。

常见坑点:

  • 忘记在 app.module.ts 注册新模块,导致依赖无法注入。
  • 控制器或服务命名冲突,建议按功能模块分目录管理。
  • 共享代码未统一管理,建议集中放在 share 目录,便于复用和维护。
  • 全局过滤器、装饰器、拦截器、守卫、管道未注册或未正确导出,导致功能失效。
  • 中间件未在 main.ts 或模块中正确 use,导致请求未被拦截。