Express + TypeScript 实战:用 Zod 做接口参数校验并自动生成 Swagger 文档

1. 环境准备

安装核心依赖:

npm install express zod @zod-openapi/core swagger-jsdoc swagger-ui-express
npm install -D typescript ts-node @types/express

配置 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}

2. 基础 Express 应用

创建 src/app.ts

import express from 'express';

const app = express();
app.use(express.json());

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

3. Zod 参数校验

定义用户注册的校验规则 (src/schemas/user.ts):

import { z } from 'zod';

export const UserSchema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
  password: z.string().min(8),
  age: z.number().int().positive().optional()
});

export type User = z.infer<typeof UserSchema>;

4. 集成 Swagger 文档

配置 Swagger (src/swagger.ts):

import swaggerJSDoc from 'swagger-jsdoc';
import { generateSchema } from '@zod-openapi/core';
import { UserSchema } from './schemas/user';

// 将 Zod 模式转为 OpenAPI 格式
const userSchema = generateSchema(UserSchema);

const options: swaggerJSDoc.Options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'User API',
      version: '1.0.0'
    },
    components: {
      schemas: {
        User: userSchema
      }
    }
  },
  apis: ['./src/**/*.ts']
};

export const swaggerSpec = swaggerJSDoc(options);

5. 创建带校验的路由

实现用户注册接口 (src/routes/user.ts):

import { Router } from 'express';
import { UserSchema } from '../schemas/user';
import { validate } from '@zod-openapi/core';

const router = Router();

/**
 * @openapi
 * /users:
 *   post:
 *     summary: 注册新用户
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/User'
 *     responses:
 *       201:
 *         description: 用户创建成功
 *       400:
 *         description: 参数校验失败
 */
router.post('/users', (req, res) => {
  const result = validate(UserSchema, req.body);
  
  if (!result.success) {
    return res.status(400).json({
      error: 'Validation failed',
      details: result.error.issues
    });
  }

  // 实际业务逻辑 (示例)
  const userData = result.data;
  res.status(201).json({
    message: 'User created',
    user: { ...userData, id: '123' }
  });
});

export default router;

6. 最终集成

更新 src/app.ts

import express from 'express';
import swaggerUI from 'swagger-ui-express';
import { swaggerSpec } from './swagger';
import userRouter from './routes/user';

const app = express();

// 路由
app.use(userRouter);

// Swagger 文档
app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(swaggerSpec));

// 其他配置...

7. 运行与验证
  1. 启动服务:
npx ts-node src/app.ts

  1. 访问文档:
http://localhost:3000/api-docs

  1. 测试接口:
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john_doe",
    "email": "john@example.com",
    "password": "secure123"
  }'

关键优势
  1. 类型安全:Zod 与 TypeScript 深度集成,提供编译时和运行时校验
  2. 文档自动化:通过 @zod-openapi/core 自动生成 OpenAPI 规范
  3. 错误处理:自动返回详细的校验错误信息
  4. 代码复用:校验逻辑和文档定义使用同一套 Schema
常见问题解决

问题: 自定义错误消息
方案: 在 Zod Schema 中配置:

z.string().min(8, { message: '密码至少8个字符' })

问题: 处理嵌套对象
方案: 使用 z.object() 嵌套:

const AddressSchema = z.object({
  city: z.string(),
  street: z.string()
});

const UserSchema = z.object({
  // ...其他字段,
  address: AddressSchema
});

问题: 文件上传校验
方案: 使用中间件处理 multipart 数据:

import multer from 'multer';
const upload = multer();

router.post('/avatar', 
  upload.single('avatar'),
  (req, res) => {
    // 处理文件
  }
);

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐