BehavioralTypeScriptverifiedVerified
Chain of Responsibility Pattern in TypeScript
Passes a request along a chain of handlers, where each handler decides to process it or pass it to the next handler in the chain.
How to Implement the Chain of Responsibility Pattern in TypeScript
1Step 1: Define the request type and abstract handler
interface Request {
type: string;
payload: unknown;
}
abstract class Handler {
private next: Handler | null = null;
setNext(handler: Handler): Handler {
this.next = handler;
return handler;
}
handle(request: Request): string | null {
if (this.next) {
return this.next.handle(request);
}
return null;
}
}2Step 2: Implement concrete handlers
class AuthHandler extends Handler {
handle(request: Request): string | null {
if (request.type === "auth") {
return "Handled by AuthHandler";
}
return super.handle(request);
}
}
class ValidationHandler extends Handler {
handle(request: Request): string | null {
if (request.type === "validate") {
return "Handled by ValidationHandler";
}
return super.handle(request);
}
}
class LoggingHandler extends Handler {
handle(request: Request): string | null {
console.log(`Logging: ${request.type}`);
return super.handle(request);
}
}3Step 3: Build the chain and process requests
const auth = new AuthHandler();
const validation = new ValidationHandler();
const logging = new LoggingHandler();
logging.setNext(auth).setNext(validation);
// Process requests
logging.handle({ type: "auth", payload: {} });
logging.handle({ type: "validate", payload: {} });// ── HTTP Middleware Chain (Express-style) ─────────────────────────
interface HttpRequest {
method: string;
path: string;
headers: Record<string, string>;
body: unknown;
context: Map<string, unknown>;
}
interface HttpResponse {
status: number;
headers: Record<string, string>;
body: unknown;
}
type NextFunction = () => Promise<HttpResponse>;
type Middleware = (
req: HttpRequest,
next: NextFunction
) => Promise<HttpResponse>;
// ── Middleware Implementations ────────────────────────────────────
const corsMiddleware: Middleware = async (req, next) => {
const response = await next();
return {
...response,
headers: {
...response.headers,
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
},
};
};
const authMiddleware: Middleware = async (req, next) => {
const token = req.headers["authorization"];
if (!token?.startsWith("Bearer ")) {
return {
status: 401,
headers: {},
body: { error: "Unauthorized" },
};
}
// Validate token and attach user to context
req.context.set("userId", "user_123");
return next();
};
const rateLimitMiddleware: Middleware = async (req, next) => {
const clientIp = req.headers["x-forwarded-for"] ?? "unknown";
const key = `rate:${clientIp}`;
// Check rate limit (simplified)
const requests = (req.context.get(key) as number) ?? 0;
if (requests > 100) {
return {
status: 429,
headers: { "Retry-After": "60" },
body: { error: "Too many requests" },
};
}
req.context.set(key, requests + 1);
return next();
};
// ── Pipeline Builder ─────────────────────────────────────────────
class MiddlewarePipeline {
private middlewares: Middleware[] = [];
use(middleware: Middleware): this {
this.middlewares.push(middleware);
return this;
}
async execute(
req: HttpRequest,
handler: (req: HttpRequest) => Promise<HttpResponse>
): Promise<HttpResponse> {
let index = 0;
const next: NextFunction = async () => {
if (index < this.middlewares.length) {
const mw = this.middlewares[index++];
return mw(req, next);
}
return handler(req);
};
return next();
}
}
// ── Usage ────────────────────────────────────────────────────────
const pipeline = new MiddlewarePipeline()
.use(corsMiddleware)
.use(rateLimitMiddleware)
.use(authMiddleware);
export {
MiddlewarePipeline,
corsMiddleware,
authMiddleware,
rateLimitMiddleware,
type Middleware,
type HttpRequest,
type HttpResponse,
};Chain of Responsibility Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Chain of Responsibility Pattern in the Real World
“Like a customer support escalation: your call starts with a front-line agent. If they can’t resolve it, they transfer you to a specialist. If the specialist can’t help, it goes to a manager. Each level either handles it or passes it up the chain.”