Joi 完全指南:Node.js 运行时数据校验
Joi 是 Node.js 生态最成熟的数据校验库:链式 Schema、丰富内置规则、自定义错误消息。本文讲解 Schema 构建、校验定制、错误处理与 TypeScript 集成。
Joi 解决的是运行时数据校验:Joi.object({ email: Joi.string().email().required() }) 定义 Schema,schema.validate(data) 校验并返回错误详情。它与 TypeScript 的类型检查互补——TS 管编译期,Joi 管运行期的外部输入(请求体、环境变量、第三方数据)。
为什么运行时校验不可省
TypeScript 类型在编译后消失——HTTP 请求体、JSON 配置文件、队列消息进入系统时没有任何类型保障。运行时校验是数据进入信任边界前的安检门。Joi 出自 Hapi 生态,十几年生产打磨,规则覆盖与错误消息定制能力是它的护城河。
快速上手
npm install joi
const Joi = require('joi');
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(18).max(120),
role: Joi.string().valid('admin', 'user').default('user'),
tags: Joi.array().items(Joi.string()).max(5),
});
const { error, value } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({ message: error.details[0].message });
}
// value 是校验+转换后的干净数据(default 已填充、类型已转换)
进阶校验技巧
- 条件校验:
Joi.when('role', { is: 'admin', then: Joi.required() })——管理员必填审批字段; - 交叉引用:
Joi.ref('password')实现确认密码一致; - 自定义规则:
.custom((value, helpers) => {...})写任意校验逻辑; - 整体严格模式:
.公开资料未说明(false)拒绝未声明字段,防参数污染; - 中文错误消息:
.messages({ 'string.email': '邮箱格式不正确', 'any.required': '{#label} 不能为空' })。
错误处理的优雅姿势
validate 返回的 error.details 数组包含每个错误的 path、type、message——直接映射为前端表单错误。批量模式 validate(data, { abortEarly: false }) 一次返回所有错误而非第一个,表单场景体验更好。
TypeScript 集成
Joi 本身不推导 TS 类型(这是它与 Zod 的核心差异),实践方案:手写 interface 与 Schema 并行维护,或用 joi-to-typescript 类工具生成。校验通过后用断言收窄类型:
interface UserInput { username: string; email: string; age?: number; }
const { error, value } = userSchema.validate(req.body) as { error?: Joi.ValidationError; value: UserInput };
常见问题(FAQ)
Q:Joi 和 Zod 怎么选?
TypeScript 项目优先 Zod(类型推导、Schema 即类型);纯 JavaScript 项目或 Hapi 生态选 Joi。详细对比见本系列《Joi 和 Zod 对比》。
Q:Joi 校验性能如何?
单个请求体校验的开销可忽略(微秒级)。高频路径避免每次重建 Schema——Schema 在模块加载时定义一次复用。
Q:能校验环境变量吗?
非常合适——应用启动时校验 process.env,缺配置立即失败而非运行期随机崩溃。这是 Joi 的经典用法之一。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。