NestJS 进阶实战教程
本教程深入讲解 NestJS 的企业级开发特性,包括数据库集成、配置管理、身份认证 (JWT)、Swagger 文档以及拦截器的使用。
目录
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)
使用 Passport 和 JWT 实现安全的登录验证。
安装:
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" } 格式中。