Express middleware và xử lý lỗi: nền tảng của một API Node.js sạch

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Express là framework web tối giản nhất của Node.js, và toàn bộ sức mạnh của nó nằm ở middleware. Khi bạn hiểu middleware chạy thế nào và lỗi chảy ra sao, việc xây một API gọn gàng, dễ bảo trì không còn khó.

Middleware là gì trong Express?

Middleware là hàm nhận (req, res, next). Nó có thể đọc hoặc sửa request, kết thúc request bằng cách trả lời, hoặc gọi next() để chuyển sang hàm tiếp theo. Các hàm chạy theo đúng thứ tự bạn khai báo.

const express = require('express');const app = express();app.use(express.json()); // đọc body JSONapp.use((req, res, next) => {  console.log(req.method, req.url);  next();});app.get('/health', (req, res) => res.json({ ok: true }));

Thứ tự rất quan trọng. Nếu đặt express.json() sau route, body sẽ không được đọc. Quên gọi next() hoặc quên trả lời khiến request treo mãi.

Middleware xác thực đơn giản

function requireAuth(req, res, next) {  const header = req.get('Authorization') || '';  const token = header.replace('Bearer ', '');  if (!token) {    return res.status(401).json({ error: 'Chưa đăng nhập' });  }  req.user = verifyToken(token); // giả sử hàm này tự viết  next();}app.get('/profile', requireAuth, (req, res) => res.json(req.user));

Gắn middleware vào từng route hoặc cả nhóm bằng express.Router() giúp bạn áp dụng quy tắc đúng chỗ cần.

Xử lý lỗi tập trung

Express nhận ra middleware xử lý lỗi nhờ có đúng bốn tham số (err, req, res, next). Nó phải được khai báo sau tất cả route:

app.use((req, res) => {  res.status(404).json({ error: 'Không tìm thấy' });});app.use((err, req, res, next) => {  console.error(err);  const status = err.status || 500;  res.status(status).json({    error: status === 500 ? 'Lỗi hệ thống' : err.message,  });});

Chú ý không trả chi tiết lỗi nội bộ (stack trace, câu SQL) cho người dùng ở production; hãy ghi log đầy đủ ở server nhưng chỉ trả thông điệp ngắn gọn.

Lỗi trong code bất đồng bộ

Với Express 4, lỗi ném ra trong hàm async không tự đến middleware xử lý lỗi, request có thể treo. Bạn cần bắt và chuyển tiếp bằng next(err), hoặc dùng một hàm bọc:

const wrap = (fn) => (req, res, next) =>  Promise.resolve(fn(req, res, next)).catch(next);app.get('/users/:id', wrap(async (req, res) => {  const user = await db.findUser(req.params.id);  if (!user) {    const e = new Error('Người dùng không tồn tại');    e.status = 404;    throw e;  }  res.json(user);}));

Express 5 đã tự chuyển lỗi từ handler async sang middleware lỗi, nên hãy kiểm tra phiên bản bạn đang dùng để biết có cần hàm bọc hay không.

Những middleware nên có

  • helmet: đặt các header bảo mật phổ biến.
  • cors: cấu hình nguồn được phép gọi API từ trình duyệt.
  • express-rate-limit: hạn chế số lần gọi, chống lạm dụng.
  • Kiểm tra dữ liệu: dùng thư viện như Zod hoặc Joi trước khi vào logic nghiệp vụ.
  • Ghi log request: như morgan hoặc pino-http.

Gợi ý cấu trúc dự án

Đừng dồn mọi thứ vào một file. Một cách tổ chức phổ biến: thư mục routes định nghĩa đường dẫn, controllers xử lý request, services chứa nghiệp vụ, middlewares chứa các hàm dùng chung. Cách chia này giúp bạn kiểm thử từng phần và thay đổi mà ít ảnh hưởng chỗ khác.

Ví dụ trả lỗi nhất quán

Một API tốt trả lỗi theo cùng một định dạng để ứng dụng khách dễ xử lý. Bạn có thể tạo một lớp lỗi riêng và để middleware lỗi biến nó thành phản hồi thống nhất:

class HttpError extends Error {  constructor(status, message, details) {    super(message);    this.status = status;    this.details = details;  }}app.use((err, req, res, next) => {  const status = err.status || 500;  res.status(status).json({    error: { message: status === 500 ? 'Lỗi hệ thống' : err.message, details: err.details },  });});

Giờ ở bất kỳ đâu trong code, bạn chỉ cần throw new HttpError(422, 'Dữ liệu không hợp lệ', {...}) và định dạng phản hồi luôn đồng nhất. Đồng đội của bạn, cũng như người viết ứng dụng di động gọi tới API, sẽ biết trước cấu trúc lỗi để xử lý.

Gợi ý thực hành

Hãy tự xây một API quản lý công việc nhỏ gồm bốn route: danh sách, tạo mới, cập nhật và xóa. Thêm middleware ghi log request, middleware kiểm tra token đơn giản và middleware lỗi tập trung. Sau đó cố tình gây lỗi ở nhiều chỗ (id không tồn tại, dữ liệu sai, lỗi không lường trước) để xem phản hồi có nhất quán không. Bài tập nhỏ này giúp bạn hiểu rõ đường đi của request và lỗi trong Express.

Kết luận

Middleware là xương sống của Express: mỗi hàm một việc nhỏ, ghép thành đường ống rõ ràng, và lỗi được gom về một chỗ. Hãy bắt đầu bằng việc thêm middleware lỗi tập trung cho API của bạn, đó là cải thiện nhỏ mang lại hiệu quả lớn.

Muốn học bài bản và có lộ trình rõ ràng? Bạn có thể xem các khóa học trên codebem.com.

author_photo
Nguyễn Đình Cường

0 comment