如何在Express.js、Prisma和Zod中实现唯一性验证?
使用Express.js、Zod和Prisma实现字段唯一性验证
你可以借助Zod的refine方法结合Prisma客户端,把唯一性验证逻辑封装到Zod Schema中,这是代码层比较优雅的实现方式,无需在业务逻辑里重复编写查询判断。
1. 为Zod Schema添加异步唯一性验证
修改你的UsersModel,给name字段添加异步refine规则,通过Prisma查询数据库判断用户名是否已存在:
import { z } from "zod"; import prisma from "./path-to-your-prisma-client"; // 导入你的Prisma客户端实例 export const UsersModel = z.object({ id: z.string().uuid(), name: z.string() .min(7).max(20) .refine(async (name) => { // 查询是否存在同名用户 const existingUser = await prisma.user.findUnique({ where: { name } }); // 返回true表示验证通过(不存在同名用户),false则验证失败 return !existingUser; }, { message: "用户名已存在,请更换其他用户名" }), password: z.string().min(7).max(20), });
2. 在Express中使用异步验证的Schema
由于refine是异步操作,确保你的请求验证中间件支持异步Zod Schema验证。如果使用zod-express-middleware这类工具,直接传入Schema即可:
import { validateRequest } from "zod-express-middleware"; import { UsersModel } from "./path-to-your-schema"; import prisma from "./path-to-your-prisma-client"; app.post("/users", validateRequest({ body: UsersModel }), async (req, res) => { // 验证通过后创建用户 const newUser = await prisma.user.create({ data: req.body }); res.status(201).json(newUser); });
3. 更新场景下的唯一性验证(排除当前用户)
如果是更新用户信息的场景,需要排除当前用户本身,避免验证自己的用户名时报错:
name: z.string() .min(7).max(20) .refine(async (name, ctx) => { // 从请求上下文获取当前用户ID(需提前在中间件中注入) const currentUserId = ctx.data.id; const existingUser = await prisma.user.findFirst({ where: { name, id: { not: currentUserId } } }); return !existingUser; }, { message: "用户名已存在,请更换其他用户名" })
注意事项
- 即使在代码层实现了唯一性验证,仍然建议在数据库层面给
name字段添加唯一索引,这可以避免并发请求场景下的竞态问题(比如两个请求同时提交相同用户名,代码层验证都通过,但数据库会拒绝重复插入)。 - 异步
refine会增加一次数据库查询,对于高并发场景可以考虑添加缓存优化,但大部分业务场景下直接查询的性能完全够用。
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

