昨天我們用 zod
做了 環境變數驗證,
今天要把這個概念延伸到 API 輸入驗證,
確保「前端送進來的資料」在到達你的程式邏輯前,
就已經是乾淨、合法、型別安全的。
typeof
。我們在 Day 15 已經安裝 express
,
這裡只要加 zod
(如果 Day 16 已裝過就不用再裝):
npm install zod
src/middleware/validate.ts
import { AnyZodObject } from "zod";
import { Request, Response, NextFunction } from "express";
export const validate =
(schema: AnyZodObject) =>
(req: Request, res: Response, next: NextFunction) => {
try {
schema.parse({
body: req.body,
query: req.query,
params: req.params,
});
next();
} catch (err: any) {
return res.status(400).json({
message: "Validation error",
errors: err.errors,
});
}
};
這個 middleware 可以驗證 body、query、params,
通過就 next()
,失敗就直接回傳錯誤。
src/routes/user.ts
import express from "express";
import { z } from "zod";
import { validate } from "../middleware/validate";
const router = express.Router();
// 驗證 schema
const getUserSchema = z.object({
params: z.object({
id: z.string().uuid(), // 必須是 UUID 格式
}),
query: z.object({
includePosts: z
.string()
.optional()
.transform(val => val === "true"), // "true" → boolean
}),
body: z.object({}), // GET 沒有 body
});
router.get(
"/:id",
validate(getUserSchema),
(req, res) => {
// 這裡的 req.params.id 已經保證是 UUID
res.json({
id: req.params.id,
includePosts: req.query.includePosts,
});
}
);
export default router;
src/index.ts
import express from "express";
import userRoutes from "./routes/user";
const app = express();
app.use(express.json());
app.use("/users", userRoutes);
app.listen(3000, () => {
console.log("🚀 Server running on http://localhost:3000");
});
GET http://localhost:3000/users/550e8400-e29b-41d4-a716-446655440000?includePosts=true
回傳:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"includePosts": true}
GET http://localhost:3000/users/123?includePosts=true
回傳:
{
"message": "Validation error",
"errors": [
{
"code": "invalid_string",
"message": "Invalid uuid",
"path": ["params","id"]
}
]
}
zod
schema 推論,免重複定義