patterntypescriptModerate
Chain of Responsibility: Process Requests Through a Handler Pipeline
Viewed 0 times
chain of responsibilitymiddleware pipelinehandler chainexpress middlewarerequest processingnext function
Problem
A request must pass through a variable number of processing steps (auth, validation, logging, rate limiting) without the sender knowing which handler will process it or how many handlers exist.
Solution
Each handler holds a reference to the next handler. It processes the request, then passes it along or stops the chain.
type Request = { user?: string; body: unknown };
type Next = (req: Request) => Promise<void>;
type Handler = (req: Request, next: Next) => Promise<void>;
function chain(...handlers: Handler[]): (req: Request) => Promise<void> {
return async (req: Request) => {
let index = 0;
const dispatch = async (i: number): Promise<void> => {
if (i >= handlers.length) return;
await handlers[i](req, () => dispatch(i + 1));
};
await dispatch(0);
};
}
const authHandler: Handler = async (req, next) => {
if (!req.user) throw new Error('Unauthorized');
await next(req);
};
const logHandler: Handler = async (req, next) => {
console.log('Request:', req.user);
await next(req);
console.log('Response sent');
};
const handle = chain(logHandler, authHandler);
await handle({ user: 'alice', body: {} });Why
This is exactly how Express/Koa/Hono middleware works. It allows adding, removing, and reordering cross-cutting concerns without modifying business logic.
Gotchas
- If no handler processes the request and you expect one to, add a fallback handler at the end that throws or returns 404.
- Forgetting to call
next()silently stops the chain — this is intentional for auth checks but a common bug in logging middleware. - Async chains require all handlers to be async-aware. A sync handler calling
next()won't await async downstream handlers.
Revisions (0)
No revisions yet.