返回首页

NestJS 进阶实战教程

NestJS 进阶实战教程

本教程深入讲解 NestJS 的企业级开发特性,包括数据库集成、配置管理、身份认证 (JWT)、Swagger 文档以及拦截器的使用。


目录

  1. 配置管理 (Config)
  2. 数据库集成 (TypeORM)
  3. 身份认证 (Auth & JWT)
  4. API 文档 (Swagger)
  5. 拦截器 (Interceptors)

1. 配置管理 (Config)

在生产环境中,数据库密码等敏感信息不应硬编码。NestJS 提供了 @nestjs/config 来管理环境变量。

安装:

npm install @nestjs/config

配置 (app.module.ts):

import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 设置为全局模块,无需在其他模块再次导入
      envFilePath: ".env",
    }),
  ],
})
export class AppModule {}

使用 (src/app.service.ts):

import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";

@Injectable()
export class AppService {
  constructor(private configService: ConfigService) {}

  getDatabaseHost(): string {
    // 读取 .env 中的 DB_HOST
    return this.configService.get<string>("DB_HOST");
  }
}

2. 数据库集成 (TypeORM)

NestJS 官方支持 TypeORM,非常适合处理关系型数据库 (MySQL, PostgreSQL)。

安装:

npm install @nestjs/typeorm typeorm mysql2

2.1 数据库连接配置

(app.module.ts):

import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./users/entities/user.entity";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: "mysql",
      host: "localhost",
      port: 3306,
      username: "root",
      password: "password",
      database: "test_db",
      entities: [User], // 注册实体
      synchronize: true, // 开发环境自动同步表结构,生产环境请关闭
    }),
    UsersModule,
  ],
})
export class AppModule {}

2.2 定义实体 (Entity)

(src/users/entities/user.entity.ts):

import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column({ select: false }) // 查询时默认不返回密码
  password: string;

  @Column({ default: true })
  isActive: boolean;
}

2.3 在 Service 中注入 Repository

(src/users/users.service.ts):

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./entities/user.entity";

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>
  ) {}

  findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  async create(userData: Partial<User>): Promise<User> {
    const user = this.usersRepository.create(userData);
    return this.usersRepository.save(user);
  }
}

不要忘记在 UsersModule 中导入 TypeOrmModule.forFeature([User])


3. 身份认证 (Auth & JWT)

使用 PassportJWT 实现安全的登录验证。

安装:

npm install @nestjs/passport passport passport-local passport-jwt @nestjs/jwt
npm install -D @types/passport-local @types/passport-jwt

3.1 创建 Auth 模块

nest g module auth
nest g service auth

3.2 配置 JWT 和策略

(src/auth/auth.module.ts):

import { Module } from "@nestjs/common";
import { AuthService } from "./auth.service";
import { UsersModule } from "../users/users.module";
import { PassportModule } from "@nestjs/passport";
import { JwtModule } from "@nestjs/jwt";
import { JwtStrategy } from "./jwt.strategy";

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: "secretKey", // 实际项目中请使用环境变量
      signOptions: { expiresIn: "60s" },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

3.3 实现 JWT 策略

(src/auth/jwt.strategy.ts):

import { ExtractJwt, Strategy } from "passport-jwt";
import { PassportStrategy } from "@nestjs/passport";
import { Injectable } from "@nestjs/common";

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: "secretKey",
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

3.4 登录与生成 Token

(src/auth/auth.service.ts):

import { Injectable } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { UsersService } from "../users/users.service";

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService
  ) {}

  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === pass) {
      // 实际应使用 bcrypt 比较哈希
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any) {
    const payload = { username: user.username, sub: user.id };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

3.5 保护路由

使用 @UseGuards(AuthGuard('jwt')) 保护控制器。

import { Controller, Get, UseGuards, Request } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";

@Controller("profile")
export class ProfileController {
  @UseGuards(AuthGuard("jwt"))
  @Get()
  getProfile(@Request() req) {
    return req.user; // 返回 JWT 解析后的用户信息
  }
}

4. API 文档 (Swagger)

NestJS 可以自动生成符合 OpenAPI (Swagger) 标准的文档。

安装:

npm install @nestjs/swagger swagger-ui-express

配置 (main.ts):

import { NestFactory } from "@nestjs/core";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle("My API")
    .setDescription("The API description")
    .setVersion("1.0")
    .addBearerAuth() // 添加 Bearer Token 认证支持
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup("api", app, document); // 访问地址: /api

  await app.listen(3000);
}
bootstrap();

装饰 DTO: 在 DTO 属性上添加 @ApiProperty(),文档会自动展示字段类型和示例。

import { ApiProperty } from "@nestjs/swagger";

export class CreateUserDto {
  @ApiProperty({ example: "Alice", description: "用户名" })
  username: string;
}

5. 拦截器 (Interceptors)

拦截器可以拦截请求和响应,用于日志记录、响应转换等。

实战:统一响应格式拦截器

(src/common/interceptors/transform.interceptor.ts):

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

export interface Response<T> {
  data: T;
  statusCode: number;
  message: string;
}

@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, Response<T>>
{
  intercept(
    context: ExecutionContext,
    next: CallHandler
  ): Observable<Response<T>> {
    return next.handle().pipe(
      map((data) => ({
        data,
        statusCode: context.switchToHttp().getResponse().statusCode,
        message: "Success",
      }))
    );
  }
}

全局启用 (main.ts):

app.useGlobalInterceptors(new TransformInterceptor());

启用后,所有 API 返回的数据都会被包裹在 { data: ..., statusCode: 200, message: "Success" } 格式中。