Việc xác thực dữ liệu đầu vào từ phía giao diện người dùng (client) là cực kỳ quan trọng, trong Node.js, có khá nhiều thư viện để làm việc này, có thể bạn đã quen sử dụng Joi hay Yup để xác thực dữ liệu, tuy nhiên, trong bài viết này, mình sẽ giới thiệu và hướng dẫn bạn sử dụng Zod, đây là thư viện xác thực mình sử dụng trong hầu hết các dự án của mình, cùng xem điểm khác biệt của Zod và các thư viện khác nhé

Zod là gì?

Zod là thư viện khai báo và xác thực lược đồ dữ liệu viết bằng TypeScript, được thiết kế thân thiện với lập trình viên và cực kỳ dễ sử dụng.

Zod hoạt động với cả Node và trong trình duyệt, điểm mình thích nhất là không cần phải sử dụng TypeScript mà vẫn tận dụng được các kiểu tĩnh được suy luận ra khi xác thực lược đồ với Zod, ngoài ra rất nhiều tính năng khác, ví dụ tạo Interfaces dựa trên lược đồ đã khai báo cho phía Client, tích hợp với các thư viện quản lý form như React Hook Form, …

Khởi tạo dự án

Cùng thực hành với 1 dự án nhỏ để tìm hiểu về Zod

Tạo thư mục dự án

mkdir zod-example
cd zod-example

Khởi tạo project và cài đặt dependencies

npm init -y
npm i express zod
npm i -D nodemon

Thêm script khởi chạy server

{
  // ...
  "scripts": {
    "start": "nodemon src/server.js"
  },
  // ...
}
package.json

Tạo server express

Tạo server express đơn giản

const express = require("express");

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

app.get("/", (req, res, next) => {
    return res.status(200).json({
        message: "Hello",
    });
});

app.listen(3001, () => {
    console.log(`Server running at http://localhost:3001/`);
});
src/server.js

Khởi chạy server

npm run dev

Khai báo lược đồ với Zod

Import Zod

const express = require("express");
const { z } = require("zod");
// ...
src/server.js

Khai báo lược đồ, ở đây mình sử dụng một lược đồ đơn giản để xác thực dữ liệu người dùng khi thực hiện đăng nhập

// ...
const loginSchema = z.object({
    body: z.object({
        username: z.string({
            required_error: "Username is required",
        }),
        password: z
            .string({
                required_error: "Password is required",
            }),
    }),
});
// ...
src/server.js

Trong đoạn mã trên:

  • loginSchema là object mô tả lược đồ dữ liệu từ request
  • body là object mô tả dữ liệu cần có trong request.body, có thể khai báo dữ liệu với các trường tương tự trong headers, query, params
  • Hai trường usernamepassword là 2 chuỗi, với thông báo lỗi tùy chỉnh được cung cấp

Xác thực dữ liệu với lược đồ đã khai báo

Thêm một API để đăng nhập và sử dụng lược đồ loginSchema đã khai báo ở trên để xác thực dữ liệu trong request

app.post("/users/login", (req, res, next) => {
  const { body } = loginSchema.parse(req);

  return res.status(200).json({
     data
  });
})
src/server.js

Ở đây có 2 điểm mình thích:

  • Nếu dữ liệu hợp lệ, thì kiểu dữ liệu của body sẽ được tự động suy ra từ lược đồ
    kiểu dữ liệu của body được suy ra từ lược đồ
  • Nếu dữ liệu không hợp lệ, Zod tự ném ra lỗi, chỉ cần sử dụng 1 hàm bắt lỗi chung để xử lý và trả về thông báo lỗi, hỗ trợ hàm util để định dạng lỗi giúp dễ dàng hiển thị phía giao diện hơn

Thêm global error handler

// ...
app.use((error, req, res, next) => {
    if (error instanceof z.ZodError) {
        const errors = error.format();

        return res.status(400).json(errors);
    } else {
        return res.status(500).json({
            error: "Something wrong :(",
        });
    }
});
// ...
src/server.js

Và thế là xong, cùng test API thôi, đây là kết quả khi gọi API mà không có dữ liệu trong body

{
    "_errors": [],
    "body": {
        "_errors": [],
        "username": {
            "_errors": [
                "Username is required"
            ]
        },
        "password": {
            "_errors": [
                "Password is required"
            ]
        }
    }
}

Còn đây là khi dữ liệu hợp lệ

{
    "data": {
        "body": {
            "username": "banx9x",
            "password": "123456"
        }
    }
}

Tổng kết

Như các bạn thấy, sử dụng Zod giúp thao tác xác thực dữ liệu được tối giản đi khá nhiều, ngoài ra với TypeScript, kiểu dữ liệu được suy luận giúp viết mã dễ dàng và an toàn hơn.

Tất nhiên đây chỉ là một ví dụ đơn giản, còn rất nhiều tính năng thú vị để bạn khám phá nữa, tìm hiểu chi tiết hơn về Zod tại đây. Good luck 🤟!