Beginner · APIs and request handling · topic 11 of 54 · 25 min
Handling errors well
Explanation
Two kinds of errors
An expected error is one you plan for: a missing product (404), a wrong field (400 or 422), a user who is not logged in (401). An unexpected error is a surprise: a bug, a typo, or a database that is down. Both need a clear response, but they are handled differently.
One error shape for the whole API
Every error response should have the same shape, so the app can show errors with one piece of code. A common shape is an error object with a code (a fixed word in capitals that programs can check) and a message (a sentence for people).
{
"error": {
"code": "NOT_FOUND",
"message": "Product 57 was not found"
}
}function errorResponse(status, code, message) {
return { status, body: { error: { code, message } } };
}
console.log(errorResponse(404, 'NOT_FOUND', 'Product 57 was not found'));
console.log(errorResponse(401, 'NOT_LOGGED_IN', 'Please log in first'));Catching unexpected errors
If a handler throws an error and nobody catches it, the request may hang or the whole server may crash. So we wrap every handler with try and catch. In the catch block we log the real error for developers and send a plain 500 to the user.
class HttpError extends Error {
constructor(status, code, message) {
super(message);
this.status = status;
this.code = code;
}
}
function safe(handler) {
return (request) => {
try {
return handler(request);
} catch (error) {
if (error instanceof HttpError) {
return { status: error.status, body: { error: { code: error.code, message: error.message } } };
}
console.log('LOG for developers:', error.message); // the real reason stays on the server
return { status: 500, body: { error: { code: 'INTERNAL', message: 'Something went wrong. Please try again.' } } };
}
};
}
const getOrder = safe((req) => {
if (req.id === 7) throw new HttpError(404, 'NOT_FOUND', 'Order 7 was not found');
if (req.id === 8) throw new Error('database password rejected');
return { status: 200, body: { id: req.id } };
});
console.log(getOrder({ id: 1 }).status); // 200
console.log(getOrder({ id: 7 }).status); // 404 with a clear message
console.log(getOrder({ id: 8 }).body); // plain 500 message, no password textThe not-found handler
At the end of the route table, add one last handler that catches every request no other route matched. It returns 404 with the same error shape, like { error: { code: "NOT_FOUND", message: "No route for GET /prodcts" } }. This is much better than an empty page or a crash.
Watch the lesson
Handling errors well · 1/6
Errors will happen. A good backend answers every error clearly and safely.
Prefer a video by a teacher? Find free videos on “error handling in node js express api best practices” on YouTube ↗
In real life: A train booking at peak time
At 10 a.m. Tatkal time, thousands of people book at once. The database gets slow and one query times out. The booking backend catches the error, writes the full details with request id t-90211 into its log, and answers 503 with {"error": {"code": "BUSY", "message": "Too many bookings right now. Please try again. Reference t-90211"}}. The user sees a clear message, and the engineers see the real cause in the logs.
Practice (0/3 done)
Write a function errorResponse(status, code, message) that returns { status, body: { error: { code, message } } }.
Write a function withErrors(handler) that returns a new function (request) => response. It calls handler(request) and returns its response. If the handler throws, it logs the error with console.error and returns { status: 500, body: { error: { code: "INTERNAL", message: "Something went wrong" } } }. The real error text must never be in the response.
Write a function validationToResponse(errors) that takes a list of validation errors like [{ field: "mobile", message: "..." }]. If the list is empty, return null. Otherwise return { status: 400, body: { error: { code: "VALIDATION_FAILED", message: "Some fields are not valid", fields: errors } } }.
Quick quiz
Read more (free)
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.