nestjs逆向工程Prisma与DTO

前沿

在nestjs中虽然官方推荐是 TypeORM,但是最佳实践仍然是 Prisma。
虽然使用Prisma 你可以不用定义任何实体模型,但是最好还是定义。
以下说明原因!

1. 为什么可以省略 DTO?

技术上完全可以,因为:

  • TypeScript 工具类型:可以用 OmitPick 等从 Prisma 类型中排除敏感字段
  • 类型安全:Prisma 生成的类型已经提供了基础类型检查
  • 减少代码量:少写一层抽象,更直接简单
// 完全可以这样用,不用自定义 DTO
@Post()
createUser(@Body() userInput: Omit<Prisma.UserCreateInput, 'id' | 'salt' | 'createdAt'>) {
  // 业务处理...
}

2. 为什么又建议定义 DTO?

为了更好的工程实践

🔒 安全性

// 自定义 DTO 明确指定客户端能传什么
export class CreateUserDto {
  @IsEmail()
  email: string;
  
  @MinLength(6)  // Prisma 类型没有这个!
  password: string;
}

📋 输入验证

// Prisma 类型只有基础类型,没有业务规则
export type UserCreateInput = {
  email: string;     // 任何字符串都行,包括 "invalid-email"
  password: string;  // 包括 "123" 这种弱密码
};

// DTO 有业务级验证
export class CreateUserDto {
  @IsEmail()        // 必须是邮箱格式
  email: string;
  
  @MinLength(6)     // 至少6位
  password: string;
}

📚 API 文档

// Swagger 装饰器生成文档
export class CreateUserDto {
  @ApiProperty({ description: '用户邮箱', example: 'user@example.com' })
  @IsEmail()
  email: string;
}

🎯 明确的接口契约

DTO 定义了前端与后端的明确约定,不受数据库 schema 变更的影响。

3. DTO 如何结合 Prisma 类型

最佳实践:继承 + 扩展

// 实现 Prisma 类型,添加验证和文档
export class CreateUserDto implements Pick<Prisma.UserCreateInput, 'email' | 'username'> {
  @ApiProperty()
  @IsEmail()
  email: string;

  @ApiProperty()
  @MinLength(3)
  username: string;

  @ApiProperty()
  @MinLength(6)
  password: string; // 额外字段,Prisma 类型中没有的验证规则
}

4. Entities 的本质与省略

Entities 的本质

// Entity 就是数据库所有字段的映射
export class UserEntity {
  id: number;
  email: string;
  password: string;
  salt: string;
  createdAt: Date;
  // ... 所有数据库字段
}

什么时候可以省略 Entities?

在以下情况下可以安全省略

  • ✅ 使用 Prisma 等现代 ORM
  • ✅ 主要是简单的 CRUD 操作
  • ✅ 没有复杂的业务逻辑封装需求
  • ✅ 业务逻辑直接在 Service 层处理
// 现代做法:Prisma 类型 + DTO,跳过 Entity 层
@Injectable()
export class UserService {
  async create(dto: CreateUserDto): Promise<UserResponseDto> {
    // 直接使用 Prisma,不需要 Entity 转换层
    const user = await this.prisma.user.create({
      data: {
        email: dto.email,
        password: await this.hashPassword(dto.password),
        // ... 其他字段
      }
    });
    
    return this.toResponseDto(user);
  }
}

总结

组件 是否可以省略 建议
DTO 可以,但不推荐 建议使用,为了验证、安全和文档
Entities 大多数情况可以 可以省略,除非需要复杂业务逻辑封装

现代 NestJS + Prisma 推荐架构

Controller (DTO 输入输出) 
↓  
Service (业务逻辑,直接使用 Prisma 类型)
↓  
Prisma (数据库操作)

这样既保持了类型安全,又减少了不必要的抽象层次。

补充

DTO 的命名有一套常见的约定,我来为你总结

// create-user.dto.ts
export class CreateUserDto {
  @ApiProperty()
  @IsEmail()
  email: string;

  @ApiProperty()
  @MinLength(6)
  password: string;

  @ApiProperty()
  @IsString()
  username: string;
}

// update-user.dto.ts  
export class UpdateUserDto {
  @ApiPropertyOptional()
  @IsOptional()
  @IsEmail()
  email?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  username?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  nickname?: string;
}

// find-users.dto.ts
export class FindUsersDto {
  @ApiPropertyOptional()
  @IsOptional()
  @IsNumber()
  page?: number = 1;

  @ApiPropertyOptional() 
  @IsOptional()
  @IsNumber()
  limit?: number = 10;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  search?: string;
}

// user-response.dto.ts
export class UserResponseDto {
  @ApiProperty()
  id: number;

  @ApiProperty()
  email: string;

  @ApiProperty()
  username: string;

  @ApiProperty()
  createdAt: Date;
}
posted @ 2025-11-18 23:27  丁少华  阅读(16)  评论(0)    收藏  举报