如何实现类似Sequelize的Prisma运行时模型验证?
在Prisma中实现类似Sequelize的运行时模型验证
可以实现,但Prisma本身没有像Sequelize那样在模型定义中直接内置validate配置的原生支持,需要通过以下几种方式达成类似效果:
1. 先通过数据库级约束做基础验证
Prisma的Schema可以定义数据库层面的约束,这些约束会同步到数据库,运行时数据库会自动校验,Prisma会抛出对应的错误。这是最底层的保障,对应你Sequelize示例中的allowNull、字段长度等规则:
model User { id Int @id @default(autoincrement()) name String @notNull // 对应allowNull: false role String @notNull @db.VarChar(20) // 限制长度为20 email String @notNull @db.VarChar(100) // 限制长度1-100 password String? // Prisma无VIRTUAL类型,虚拟字段可在DTO层处理 createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") @@map("users") @@index([deletedAt]) // 适配软删除逻辑 }
注意:数据库约束的错误信息是数据库原生返回的,可能不够友好,需要额外处理错误信息。
2. 使用Prisma Middleware实现全局运行时验证
这是最接近Sequelize模型内验证的方式,通过中间件拦截Prisma Client的所有操作,在执行前自定义校验逻辑。可以复用你已有的class-validator,或者用其他验证库:
import { PrismaClient } from '@prisma/client'; import { validate, IsNotEmpty, IsEmail, Length } from 'class-validator'; import { plainToInstance } from 'class-transformer'; // 定义验证类,复用class-validator规则 class UserValidator { @IsNotEmpty({ message: 'name cannot be empty' }) name: string; @IsNotEmpty({ message: 'role cannot be empty' }) @Length(1, 20, { message: 'role length should be 1 to 20 characters' }) role: string; @IsNotEmpty({ message: 'Email cannot be empty' }) @IsEmail({}, { message: 'Invalid email' }) @Length(1, 100, { message: 'Email length should be 1 to 100 characters' }) email: string; } const prisma = new PrismaClient(); // 注册中间件 prisma.$use(async (params, next) => { // 只拦截User模型的create和update操作 if (params.model === 'User' && ['create', 'update'].includes(params.action)) { // 整理要验证的数据(update操作可能是部分字段,需补全原有数据) const targetData = params.action === 'create' ? params.args.data : { ...await prisma.user.findUnique({ where: params.args.where }), ...params.args.data }; // 执行验证 const validatorInstance = plainToInstance(UserValidator, targetData); const errors = await validate(validatorInstance, { validationError: { target: false } }); if (errors.length > 0) { // 抛出整理后的错误信息 const errorMsg = errors.flatMap(err => Object.values(err.constraints)).join('; '); throw new Error(errorMsg); } } // 继续执行原Prisma操作 return next(params); });
这种方式会全局生效,所有通过Prisma Client操作User模型的请求都会经过验证,无需修改业务代码的调用方式。
3. 封装Prisma方法做业务级验证
如果需要将验证和业务逻辑绑定,可以封装一层业务方法,在方法内先做验证,再调用Prisma:
import { PrismaClient, User } from '@prisma/client'; const prisma = new PrismaClient(); export async function createUser(data: Omit<User, 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'>) { // 自定义验证逻辑 if (!data.name) throw new Error('name cannot be empty'); if (data.role.length < 1 || data.role.length > 20) throw new Error('role length should be 1 to 20 characters'); if (!/^\S+@\S+\.\S+$/.test(data.email)) throw new Error('Invalid email'); if (data.email.length < 1 || data.email.length > 100) throw new Error('Email length should be 1 to 100 characters'); // 验证通过后调用Prisma return prisma.user.create({ data }); }
这种方式灵活度高,但需要确保所有User模型的操作都通过封装方法调用,避免直接使用Prisma Client跳过验证。
内容的提问来源于stack exchange,提问作者david Billian
相关产品推荐
相关产品推荐

