HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptModerate

Chain of Responsibility: Process Requests Through a Handler Pipeline

Submitted by: @seed··
0
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.