Skip to contentJMRG
All posts

Chain of Responsibility Pattern: Processing Pipelines

design-patternsbehavioralchain-of-responsibility

Intent

Chain of Responsibility avoids coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. It chains the receiving objects and passes the request along the chain.

The Problem

Without this pattern, validation code becomes endless if-else:

function handleRequest(req: Request) {
  if (!req.headers.auth) {
    return { status: 401 };
  }
  if (rateLimiter.isExceeded(req.ip)) {
    return { status: 429 };
  }
  // Finally, business logic...
}

The Solution: Chain of Responsibility

classDiagram
    class Handler {
        <<interface>>
        +setNext(handler): Handler
        +handle(request): Response
    }

    class BaseHandler {
        -next: Handler
        +handle(request): Response
    }

    Handler <|.. BaseHandler
    BaseHandler o--> Handler

TypeScript Implementation

abstract class MiddlewareHandler {
  private next: MiddlewareHandler | null = null;

  setNext(handler: MiddlewareHandler): MiddlewareHandler {
    this.next = handler;
    return handler;
  }

  async handle(ctx: HttpContext): Promise<void> {
    const shouldContinue = await this.process(ctx);
    if (shouldContinue && this.next) {
      await this.next.handle(ctx);
    }
  }

  protected abstract process(ctx: HttpContext): Promise<boolean>;
}

class AuthMiddleware extends MiddlewareHandler {
  protected async process(ctx: HttpContext): Promise<boolean> {
    if (!ctx.request.headers['authorization']) {
      ctx.response.status = 401;
      return false;
    }
    return true;
  }
}

class MiddlewarePipeline {
  private first: MiddlewareHandler | null = null;
  private last: MiddlewareHandler | null = null;

  use(handler: MiddlewareHandler): this {
    if (!this.first) {
      this.first = this.last = handler;
    } else {
      this.last!.setNext(handler);
      this.last = handler;
    }
    return this;
  }
}

When to Use

Situation Recommendation
HTTP middlewares ✅ Recommended
Layered validation ✅ Recommended
Event processing ✅ Recommended
Simple linear flow ❌ Consider alternatives

Conclusion

Chain of Responsibility allows building flexible pipelines where each handler has a clear responsibility.