0

Error Handler với NodeJS

1. Error Handler là gì?

Error Handler là cơ chế giúp ứng dụng phát hiện, tiếp nhận và xử lý lỗi tập trung.

Thay vì mỗi Controller phải tự viết try...catch và trả về lỗi, Express cho phép chuyển tất cả lỗi về một Error Middleware để xử lý thống nhất.

Mục đích:

  • Tránh lặp code xử lý lỗi ở nhiều Controller.
  • Phân biệt các loại lỗi bằng HTTP Status Code.
  • Chuẩn hóa response trả về Client.
  • Giúp code dễ bảo trì và debug hơn.

2. Error Handling hoạt động như thế nào?

Trong dự án, cơ chế xử lý lỗi được chia thành 3 phần:

Thành phần Nhiệm vụ
Custom Error Định nghĩa các loại lỗi
Async Handler Bắt lỗi từ hàm async
Error Middleware Xử lý lỗi và trả response

Bước 1: Custom Error – Định nghĩa lỗi

Vấn đề: Nếu sử dụng new Error() thông thường, chúng ta chỉ có thông báo lỗi mà chưa có HTTP Status Code tương ứng.

Giải pháp: Tạo các Custom Error Class kế thừa từ Error để mỗi loại lỗi có sẵn Status Code.

Ví dụ:

class ErrorResponse extends Error {
    constructor(message, statusCode) {
        super(message)
        this.status = statusCode
    }
}

class NotFoundError extends ErrorResponse {
    constructor(message = 'Not Found') {
        super(message, 404)
    }
}

Một số loại lỗi thường dùng:

Error Class Status Khi nào sử dụng?
BadRequestError 400 Request không hợp lệ
AuthFailureError 401 Xác thực thất bại
ForbiddenError 403 Không có quyền truy cập
NotFoundError 404 Không tìm thấy dữ liệu
ConflictRequestError 409 Dữ liệu bị xung đột

Ghi nhớ: Custom Error giúp chúng ta chủ động xác định lỗi và Status Code ngay tại nơi phát sinh lỗi.

Bước 2: Async Handler – Bắt lỗi bất đồng bộ

Vấn đề: Controller thường sử dụng async/await để gọi Database hoặc Service. Khi xảy ra lỗi, cần chuyển lỗi đó đến Error Middleware.

Thay vì viết try...catch cho từng Controller, chúng ta tạo một hàm asyncHandler để tái sử dụng.

const asyncHandler = (fn) => {
    return (req, res, next) => {
        fn(req, res, next).catch(next)
    }
}

Cách sử dụng:

router.post(
    '/shop/login',
    asyncHandler(accessController.login)
)

Cách hoạt động:

  1. asyncHandler nhận một Controller.
  2. Gọi Controller khi có request.
  3. Nếu Promise bị reject, .catch(next) sẽ chuyển lỗi cho Express.
  4. Express tiếp tục chuyển lỗi đến Error Middleware.

Ghi nhớ: asyncHandler không trực tiếp xử lý lỗi. Nó chỉ có nhiệm vụ bắt lỗi và chuyển tiếp lỗi.

Bước 3: Error Middleware – Xử lý lỗi tập trung

Sau khi nhận lỗi từ asyncHandler, Express chuyển lỗi đến Error Middleware.

app.use((error, req, res, next) => {
    const statusCode = error.status || 500

    return res.status(statusCode).json({
        status: 'error',
        code: statusCode,
        message: error.message || 'Internal Server Error'
    })
})

Nhiệm vụ:

  • Nhận lỗi từ next(error).
  • Lấy HTTP Status Code từ lỗi.
  • Nếu không có Status Code thì mặc định là 500.
  • Trả response JSON thống nhất cho Client.

Lưu ý:

  • Error Middleware phải có đủ 4 tham số: (error, req, res, next).
  • Phải đặt sau tất cả các Routes để có thể tiếp nhận lỗi từ chúng.

3. Thử hình dung qua một ví dụ thực tế

Giả sử người dùng đăng nhập nhưng nhập sai mật khẩu.

Quá trình xử lý diễn ra như sau:

  1. Client: Gửi request đăng nhập.
  2. Router: Gọi Controller thông qua asyncHandler.
  3. Controller: Gọi AccessService.signIn().
  4. Service: Kiểm tra mật khẩu. Nếu sai, ném lỗi:
throw new AuthFailureError('Authentication error')
  1. Async Handler: Bắt lỗi bằng .catch(next).
  2. Error Middleware: Nhận lỗi và trả về HTTP 401.
  3. Client: Nhận response:
{
    "status": "error",
    "code": 401,
    "message": "Authentication error"
}

4. Tổng kết

Thành phần Hiểu đơn giản
Custom Error Tạo ra lỗi có thông tin cụ thể và Status Code tương ứng thay vì sử dụng lỗi mặc định
throw Chủ động ném lỗi và dừng luồng chạy hiện tại
asyncHandler Bắt lỗi tự động bắt trọn mọi lỗi được ném ra trong các controller bất đồng bộ mà không cần phải viết try/catch lặp đi lặp lại.
next(error) Chuyển lỗi đến Error Middleware
Error Middleware Xử lý lỗi và trả response

Luồng xử lý lỗi:

image.png

Nguyên tắc: Không cần trả response ở nơi phát sinh lỗi. Hãy để Error Middleware xử lý lỗi.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí