😲Zod là gì? Hướng dẫn Validation với Zod (ok)
https://200lab.io/blog/zod-la-gi
Last updated
Was this helpful?
https://200lab.io/blog/zod-la-gi
Last updated
Was this helpful?
app\page.tsx
app\Test.tsx
app\page.tsx
app\page.tsx
app\Test.tsx
Với transform bạn có thể biến đổi dữ liệu sau khi xác thực. Ví dụ, bạn muốn email luôn ở dạng lowercase:
app\page.tsx
app\Test.tsx
app\api\register\route.ts
lib\schemas\register-schema.ts
Check đúng
lib\schemas\register-schema.ts
Zod là một thư viện JavaScript/TypeScript kiểm tra kiểu dữ liệu, giúp bạn xây dựng các schema mô tả format dữ liệu theo định dạng bạn mong muốn
Mục Lục
Chắc hẳn khi làm website, bạn đã từng đau đầu với việc dữ liệu "rác" được gửi từ client lên server: email sai định dạng, password quá ngắn, không đúng format bạn mong muốn, tên trống trơn,.... Những vấn đề tưởng chừng nhỏ nhặt này nhưng nếu không xử lý, chúng có thể gây ra siêu nhiều lỗi khó chịu, mất thời gian debug, hoặc thậm chí gây nguy cơ về bảo mật.
Trong TypeScript và Next.js, bạn có thể dễ dàng tạo API routes phục vụ phía frontend. Tuy nhiên, để đảm bảo dữ liệu gửi lên luôn đúng format, bạn cần một giải pháp xác thực dữ liệu (validation). Và đó chính là lý do bạn cần đến Zod. Vậy thì Zod là gì? Hãy cùng mình đi vọc nó qua bài này nha.
1. Zod là gì?
Zod là một thư viện JavaScript/TypeScript kiểm tra kiểu dữ liệu (type validation), giúp bạn xây dựng các schema mô tả format dữ liệu theo định dạng bạn mong muốn, sau đó dùng schema này để kiểm tra (parse) dữ liệu thực tế. Nếu dữ liệu không khớp với schema, Zod sẽ cho bạn biết ngay cái gì sai.
Bên cạnh đó, Zod giúp bạn tránh các bug do dữ liệu sai kiểu, đảm bảo tính nhất quán kiểu dữ liệu giữa client và server, code của bạn sẽ trông gọn gàng, dễ bảo trì.
Hiểu đơn giản như thế này:
Bạn định nghĩa "Cấu trúc dữ liệu" bạn mong muốn (VD: email phải là chuỗi hợp lệ, password >= 6 ký tự).
Khi dữ liệu từ form hoặc API gửi lên, bạn cho nó đi qua Zod.
Nếu hợp lệ, thì bạn sẽ sử dụng dữ liệu đó.
Nếu không, bạn sẽ trả về lỗi, yêu cầu người dùng nhập lại.
Zod cũng tích hợp rất ngon với TypeScript, cho phép bạn suy luận kiểu (type inference) trực tiếp từ schema mà không phải viết đi viết lại kiểu bằng tay.
2. Tại sao nên dùng Zod trong dự án Next.js?
Next.js rất phổ biến và thường được sử dụng với TypeScript. Khi kết hợp Zod, bạn có thể:
Dễ dàng xác thực dữ liệu ngay trong API routes.
Chia sẻ schema giữa client (frontend) và server (backend) trong cùng một codebase.
Loại bỏ nhiều lỗi runtime vì dữ liệu luôn được kiểm tra cẩn thận.
Loại bỏ "if/else" kiểm tra dữ liệu rườm rà. Thay vì bạn phải code như thế này:
TS
Bạn chỉ cần định nghĩa một schema và parse nó. Việc check Zod sẽ đảm nhận, code trở nên rõ ràng và ngắn gọn hơn nhiều.
Dễ dàng tích hợp với React Hook Form (hoặc các thư viện form khác)
Zod có thể kết hợp với React Hook Form thông qua @hookform/resolvers/zod
. Nhờ đó, bạn xác thực form ngay ở phía client, hiển thị lỗi cho người dùng real-time mà không cần chờ đến lúc người dùng submit.
3. Những khái niệm cơ bản trong Zod
Schema trong Zod giống như một "bản vẽ" dữ liệu. Bạn nói: "Tôi muốn một object có trường email là chuỗi email hợp lệ, password là chuỗi >= 6 ký tự". Dựa trên schema, Zod sẽ kiểm tra dữ liệu thực tế.
Ví dụ một schema đơn giản cho việc đăng ký tài khoản (gồm email, password, name)
TS
Bây giờ, thử parse một object:
TS
Khi bạn chạy chương trình (giả sử trong môi trường node hoặc Next.js API), bạn sẽ nhận được parsedData đúng với kiểu bạn mong muốn. Nếu thử email: "abc" (không phải email hợp lệ), Zod sẽ ném lỗi kèm thông báo: Email không hợp lệ.
z.string()
cho chuỗi
z.number()
cho số
z.boolean()
cho boolean
z.object({...})
cho object
z.array(subSchema)
cho mảng
z.enum([...])
cho enum
Và nhiều kiểu khác như là: date, literal, union, intersection...)
schema.parse(data)
: nếu dữ liệu sai, nó sẽ throw lỗi (throw error).
schema.safeParse(data)
: không ném lỗi, mà trả về một object { success: boolean; data?: T; error?: ZodError }.
Cá nhân mình thấy, nếu mà bạn đang ở phía server (API route), safeParse thường tiện hơn vì bạn có thể xử lý lỗi mà không cần phải try/catch.
Bạn có thể lấy kiểu TypeScript từ schema bằng z.infer
TS
Điều này giúp bạn không phải viết lại interface hay type cách thủ công.
Hoặc bạn có thể định nghĩa Schema bằng Zod, suy ra kiểu TypeScript tương ứng và dùng nó ở bất kỳ đâu
TS
u ở đây chắc chắn phải có id: number
và email: string
. Bạn không phải lo lắng về việc id là kiểu string hay email bị để trống. Mọi thứ được đảm bảo bởi Zod ở runtime và xác nhận bởi TypeScript ở compile-time.
Zod không chỉ dừng lại ở việc check kiểu dữ liệu. Bạn còn có thể thêm logic phức tạp hơn bằng refine, hoặc thay đổi dữ liệu sau khi parse bằng transform.
Ví dụ, bạn có 2 trường password và confirmPassword. Bạn muốn chắc chắn rằng chúng giống nhau. Bạn có thể dùng refine:
TS
Với transform bạn có thể biến đổi dữ liệu sau khi xác thực. Ví dụ, bạn muốn email luôn ở dạng lowercase:
TS
Sau khi parse, email trả về chắc chắn lowercase. Điều này tiện nếu bạn muốn normalize dữ liệu đầu vào.
4. Tích hợp Zod vào API Routes Nextjs
Trong Next.js, bạn có thư mục pages/api
để tạo API routes. Ví dụ, bạn tạo file pages/api/register.ts
TS
Khi client gửi request POST với dữ liệu hợp lệ, bạn trả về 200. Nếu dữ liệu sai, trả về 400 kèm chi tiết lỗi. Việc check dữ liệu gọn gàng hơn nhiều so với dùng if/else phải không?
Cách bên trên là sử dụng với Page Router, ở Nextjs 13 trở lên bạn có thể dùng App Router. App Router cho phép bạn định nghĩa route API dưới dạng file route.ts (hoặc route.js) trong folder app/. Việc xử lý request/response cũng khác một chút: bạn sẽ làm việc với Request Web API (chuẩn của browser/Node) và trả về Response.
file app/api/register/route.ts
TS
Không dùng NextApiRequest, NextApiResponse: Với App Router, hàm xử lý request là các hàm GET, POST, PUT, DELETE… tương ứng, chứ không phải là một hàm duy nhất export default như trước.
Dùng Request Web API: lúc này request là một instance của Request (theo Web API). Để lấy dữ liệu JSON từ body, bạn gọi await request.json().
Trả về Response hoặc NextResponse: thay vì res.status(200).json(...), bạn dùng NextResponse.json(data, { status: ... }) để trả về JSON response. NextResponse được Next.js cung cấp giúp việc tạo response dễ dàng hơn. Bạn cũng có thể dùng new Response(JSON.stringify(...), {status: ...}) nếu muốn.
Kiểm tra method: vì bạn đặt hàm POST nên route này mặc định xử lý method POST. Nếu bạn muốn hỗ trợ nhiều method, bạn có thể export nhiều hàm tương ứng như export async function GET(...) { ... }, export async function POST(...) { ... },...
5. Xác thực dữ liệu trên Frontend (trong component React)
Khi xây dựng form đăng ký trên frontend (trong Next.js), bạn có thể kết hợp Zod với React Hook Form - một thư viện form phổ biến, nhẹ, dễ dùng.
TSX
Ở đây, zodResolver sẽ dùng schema registerSchema để xác thực dữ liệu form trước khi gửi lên server. Nếu email không hợp lệ hoặc password quá ngắn, lỗi sẽ hiển thị ngay lập tức bên dưới trường input tương ứng.
Điều này mang lại trải nghiệm tốt cho người dùng: họ biết sai ở đâu và sửa ngay, thay vì gửi form xong mới nhận về thông báo lỗi.
6. Một vài lời khuyên khi sử dụng Zod
Tách schemas ra thành file riêng: như ví dụ ở trên, cho schema vào lib/schemas/ để dễ tìm, dễ quản lý.
Tái sử dụng schema giữa client và server: đừng định nghĩa lại kiểu dữ liệu ở nhiều nơi. Chỉ cần 1 schema duy nhất, import và dùng cả ở frontend lẫn backend.
Sử dụng TypeScript inference: luôn type MyType = z.infer<typeof mySchema>
để tránh sai sót và thừa.
Sử dụng refine và transform khi cần: khi logic phức tạp hơn, hãy tận dụng refine. Khi cần chuẩn hóa dữ liệu (VD: trim chuỗi, lowercase email), hãy dùng transform.
Xử lý lỗi gọn gàng: trả về lỗi dạng JSON rõ ràng cho client. Người dùng muốn biết họ sai ở đâu để sửa mà.
7. Kết luận
Zod không chỉ hữu ích trong dự án Next.js mà còn dùng tốt ở các dự án Node.js, React, hay bất cứ nơi nào bạn cần xác thực dữ liệu. Hãy thử áp dụng nó vào project của bạn, bạn sẽ thấy code nhìn clean hơn, yên tâm hơn khi dữ liệu được kiểm soát chặt chẽ.
Qua bài này, hy vọng bạn đã hiểu rõ về khái niệm cũng như các chức năng, sử dụng Zod trong dự án thế nào.