Cần, và đây là bắt buộc tuyệt đối. Validate ở client là trải nghiệm người dùng, không phải kiểm soát bảo mật — client nằm hoàn toàn trong tay người dùng, ai cũng gọi thẳng API bằng curl hay Postman được, bỏ qua toàn bộ form.
Nguyên tắc validate ở server:
- Allowlist, không blocklist. Mô tả cái gì hợp lệ (kiểu, khoảng giá trị, độ dài, tập giá trị cho phép) thay vì liệt kê cái gì bị cấm — danh sách cấm luôn thiếu.
- Parse, đừng chỉ kiểm tra. Dùng schema trả về object đã được ép kiểu và cắt gọn, rồi chỉ dùng object đó cho phần còn lại của handler.
ts
const Body = z.object({
quantity: z.number().int().min(1).max(50),
couponCode: z.string().regex(/^[A-Z0-9]{4,16}$/).optional(),
}).strict() // reject unknown keys
const input = Body.parse(await req.json()).strict()/ whitelist trường để field lạ không lọt xuống tầng dưới.- Kiểm cả ràng buộc nghiệp vụ, không chỉ hình dạng dữ liệu: giá lấy từ DB chứ không nhận từ client, số lượng không vượt tồn kho,
orderIdphải thuộc về user đang đăng nhập. - Validate ở biên của mỗi service — dữ liệu từ service nội bộ, message queue, webhook cũng là đầu vào không tin cậy.
Dùng chung schema giữa client và server là cách tốt để không lệch, nhưng chia sẻ schema không đồng nghĩa bỏ được lần chạy ở server.