Behavioral pattern
Chain of Responsibility
A request travels down a line of handlers — each one either deals with it or passes it on — so the sender never needs to know which handler will actually respond.
✗ The problem
One giant function, tangled with every concern
Auth, rate-limiting, validation and the actual handling all live in nested
if/else blocks. You can't reorder, reuse, or remove a single step in isolation.
function handleRequest(req) {
if (checkAuth(req)) {
if (checkRateLimit(req)) {
if (validate(req)) {
return route(req);
} else return 400;
} else return 429;
} else return 401;
// ← want a new step? squeeze in another nested branch…
}
Every concern is jammed into one place — hard to add, remove, or reorder a
single step without touching all the others.
✓ The pattern
→
→
→
A chain of handlers — each passes to the next
Every handler either short-circuits the request or forwards it to
this.next. Nobody needs to know the whole chain.
class Handler {
setNext(h) { this.next = h; return h; }
handle(req) {
return this.next ? this.next.handle(req) : null;
}
}
// Auth, RateLimit, Validate, Route each
// extend Handler and short-circuit or
// call super.handle(req) to move on.
Auth
RateLimit
Validate
Route
✓ See it live
Send a request — watch it walk the chain
A valid request flows through every handler to the end. An
unauthorized one is rejected by Auth — the rest never run.
Follow the numbered messages top-to-bottom — each handler either passes the request on to the next, or short-circuits the chain.
✓ Takeaway
Decouple sender from receiver
- The caller just fires the request at the first handler — it never knows which one (if any) ends up handling it.
- Composable: add, remove, or reorder handlers without touching the others.
- You already use it: Express/Koa middleware, servlet filters, DOM event bubbling, logging pipelines.
- Careful: if no handler deals with the request, it can fall off the end unhandled — always have a default/terminal handler.
🎯 Principle applied: each handler has one job
(SRP) and you add or reorder handlers
without touching the others (Open/Closed).
Back to all topics →