nestjs逆向工程Prisma与DTO
前沿
在nestjs中虽然官方推荐是 TypeORM,但是最佳实践仍然是 Prisma。
虽然使用Prisma 你可以不用定义任何实体模型,但是最好还是定义。
以下说明原因!
1. 为什么可以省略 DTO?
技术上完全可以,因为:
- ✅ TypeScript 工具类型:可以用
Omit、Pick等从 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;
}

浙公网安备 33010602011771号