hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Error Handling and Resilience with @hectoday/http

The problem with errors

  • Why error handling matters
  • Project setup

Error fundamentals

  • JavaScript error types
  • Try-catch and error propagation
  • Async errors

Structured error handling

  • Custom error classes
  • A global error handler
  • Operational vs programmer errors

Resilience patterns

  • Retries
  • Timeouts
  • Circuit breakers
  • Fallbacks and degradation

Server lifecycle

  • Graceful shutdown
  • Uncaught exceptions and unhandled rejections
  • Health checks under failure

Putting it all together

  • Error handling checklist
  • Capstone: resilient e-commerce API

Why error handling matters

Errors are not exceptional

Throughout this series, every course has dealt with errors in some way. The REST API Design course built error responses. The Securing Your API course handled authentication failures. The File Uploads course caught oversized files. But none of them stopped to explain the system behind all of that. That is what this course is about.

Here is the thing most beginners do not realize: errors are not rare events. They are a completely normal part of running software. Users send invalid data. Databases go down. External APIs time out. Networks drop packets. The question is never “will errors happen?” because they absolutely will. The question is “what happens when they do?”

Three things that go wrong

Let’s look at three ways errors can ruin your day. These are not hypothetical. These are things that happen in production, sometimes all at once.

The server crashes

Imagine someone requests a user profile by ID. The ID does not match any record in the database:

route.get("/users/:id", {
  resolve: (c) => {
    const user = db.prepare("SELECT * FROM users WHERE id = ?").get(c.input.params.id);
    return Response.json({ name: user.name }); // If user is undefined... crash
  },
});

What happens here? The database query returns undefined because no user matched that ID. Then we try to read user.name. You cannot read a property on undefined. JavaScript throws a TypeError: Cannot read properties of undefined. There is no check, so the error propagates all the way up the call stack. The Node.js process crashes.

Every in-flight request fails. Every connected WebSocket drops. Every user sees an error. The server restarts, but for a few seconds, your entire application was dead. All because of one missing database record.

The user sees garbage

Sometimes the error is caught, but the response is useless:

{ "error": "Internal server error" }

What is the user supposed to do with that? They do not know what went wrong. The developer does not know what went wrong either, because the real error was swallowed somewhere. Nobody can fix the problem because nobody knows what the problem is.

Secrets leak

Other times, the error response includes way too much information:

{
  "error": "SQLITE_ERROR: no such table: users",
  "stack": "Error: SQLITE_ERROR\n    at Database.prepare (/app/node_modules/better-sqlite3/...)"
}

An attacker now knows the database engine (SQLite), the library (better-sqlite3), and the internal file path. This is called information disclosure, and it is a real security vulnerability. The Web Security course covers this in detail, but the short version is: never send stack traces to the client.

What good error handling looks like

So what should happen instead? Good error handling does four things.

The server stays up. One bad request does not crash the process. The error is handled, logged, and a proper response is sent. Every other request keeps working.

The user gets useful feedback. “Email is required.” “Book not found.” “You are not authorized.” Messages that are specific, actionable, and consistent.

The developer gets context. The error is logged with the request path, method, user ID, and stack trace. When something breaks, the developer can find and fix the bug quickly.

Nothing leaks. Stack traces, database details, and internal paths stay in the logs. The client sees a clean error message and nothing more.

This course builds a systematic approach to all four of those things. We will start with the fundamentals of how JavaScript errors work, then build structured error responses that routes return directly. After that, we will wire up a safety net for unexpected errors and move into resilience patterns like retries, timeouts, and circuit breakers. By the end, you will have an API that handles failure gracefully at every layer.

Up next, we will set up the project we will build throughout this course.

Why is returning the stack trace in an error response dangerous?

Project setup →

© 2026 hectoday. All rights reserved.