Skip to content
Share
Explore

Error Handling

Xử lý Error trong Hono và Express
Bạn đang viết một REST API, mọi thứ chạy ổn. Rồi một ngày user gửi request với ID không tồn tại, token hết hạn, hoặc database bỗng dưng timeout — và app trả về… một trang lỗi trắng tinh, hoặc tệ hơn là crash luôn.
Xử lý error tốt không chỉ là try/catch cho qua — mà là biết log ở đâu, throw lên đâu, và trả về response lỗi nhất quán cho client. Bài này sẽ đi qua từng tình huống cụ thể, đối chiếu giữa Express và Hono.

Tư duy cốt lõi

Trước khi vào code, cần hiểu một nguyên tắc quan trọng:
Chỉ có handler mới có c (context) — chỉ handler mới trực tiếp trả được response.
Điều này nghĩa là:
Nếu lỗi xảy ra trong handlerreturn c.json() tại chỗ
Nếu lỗi xảy ra trong service/repository (không có c) → throw lên để handler bắt
Nếu muốn log và xử lý tập trung → dùng onError
Service/Repository Handler onError
│ │ │
│ throw Error() │ │
│──────────────────────>│ │
│ │ throw tiếp │
│ │───────────────────>│
│ │ │ log + return response

Cách 1: Xử lý lỗi tại chỗ trong Handler

Phù hợp khi logic đơn giản, lỗi có thể đoán trước ngay trong handler.
Hai cách viết gần như giống nhau, chỉ khác resc và phải return.

Cách 2: Throw từ Service, Handler bắt lại

Khi logic nằm ở service layer — không có c, không thể trả response trực tiếp.

Dùng Error thông thường

So sánh string để phân loại lỗi rất dễ gây bug — typo một chữ là sai hết.

Dùng HTTPException — Hono có sẵn

HTTPException giải quyết vấn đề trên bằng cách gắn status code trực tiếp vào error:

Custom Error Class trong Express — tương đương

Trong Express không có HTTPException sẵn, nhưng bạn có thể tự tạo tương tự:
HTTPException của Hono về bản chất cũng là pattern này — chỉ là có sẵn, không cần tự viết.

Cách 3: onError — Bắt lỗi tập trung

Khi app có nhiều routes, viết try/catch lặp đi lặp lại ở mỗi handler rất tẻ nhạt. Cả Express và Hono đều có cách bắt lỗi tập trung.
Khi dùng onError, handler không cần try/catch nữa — throw lên là onError tự bắt:
Lưu ý: Đặt app.onError ở cuối file, sau khi đã khai báo hết routes — tương tự error middleware của Express.

Kết hợp cả ba cách

Trong thực tế, bạn sẽ dùng cả ba tùy tình huống:

Best Practices

✅ Nên làm:
Dùng HTTPException (Hono) hoặc custom AppError (Express) khi throw từ service — để giữ status code theo cùng error
Đặt onError / error middleware ở cuối file, sau tất cả routes
Log error ở một chỗ duy nhất — trong onError, không phải rải rác ở từng handler
Trả về cùng một shape cho mọi error response: { message: string }
❌ Tránh:

Tóm tắt

Tình huống
Express
Hono
Lỗi đơn giản trong handler
res.status(404).json()
return c.json({}, 404)
Throw từ service kèm status
Custom AppError
HTTPException
Bắt lỗi tập trung
Error middleware (4 params)
app.onError((err, c) => {})
Log tập trung
Trong error middleware
Trong app.onError
There are no rows in this table
Nguyên tắc cốt lõi không đổi dù dùng Express hay Hono: throw lỗi có status code từ service, bắt và trả response ở handler hoặc onError, log một chỗ duy nhất.
Bước tiếp theo: Khi đã nắm error handling, bạn có thể tìm hiểu thêm về Validation với Zod (@hono/zod-validator) để bắt lỗi input từ sớm trước khi vào service, và Logging middleware để ghi lại mọi request/response một cách có hệ thống.
Want to print your doc?
This is not the way.
Try clicking the ··· in the right corner or using a keyboard shortcut (
CtrlP
) instead.