Module 43: Backend Architecture

Lesson 5 of 5 · 15–25 minutes

Error handling in one place

🏠The everyday picture

A complaints desk with clear rules: lost luggage goes here, refunds there, anything else to the manager. Staff don't each invent their own way of handling complaints.

📘What it really is

export class AppError extends Error {
  status: number;
  constructor(status: number, message: string) {
    super(message);
    this.status = status;
  }
}
export class NotFoundError extends AppError { constructor(m: string) { super(404, m); } }
export class ConflictError extends AppError { constructor(m: string) { super(409, m); } }
app.use((err, req, res, next) => {
  if (err instanceof z.ZodError) {
    return res.status(422).json({ error: "Validation failed", fields: z.flattenError(err).fieldErrors });
  }
  if (err instanceof AppError) {
    return res.status(err.status).json({ error: err.message });
  }
  console.error(err);                      // Module 45: proper logging
  res.status(500).json({ error: "Something went wrong. Please try again." });
});

🖱️Try it

Add the error classes and the error handler. Remove try/catch from controllers where the handler now covers it. Test: an unknown id (404), invalid input (422 with fields), closing a closed enquiry (409), and a deliberate bug (500 with a generic message, full details in the terminal).

✅Quick check

A database error occurs. What does the client see, and what's logged?

Show the answer

The client sees a generic 500 message; the full error and stack are logged on the server.

Why handle errors in one middleware instead of in every route?

Show the answer

Consistent responses everywhere, less repeated code, and no route can forget.

← PreviousValidation at the edge
ProLearners Assist