Skip to content

Class: Runner<MiddlewareFn>

Runner executes an array of middleware functions in sequence.

The middleware pipeline advances only when the current function calls next, implementing the chain of responsibility pattern. You control how to execute the underlying middleware functions by providing an executor function.

Example

ts
const context = { stack: [] };
type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>;

const middleware = new Middleware<MiddlewareFn>();
middleware.add((ctx, next) => {
  ctx.stack.push("fn1");
  await next();
});

await middleware
  .runner()
  .finalHandler(() => {
    context.stack.push("final handler");
  })
  .run((fn, next) => fn(context, next));

Type Parameters

Type ParameterDescription
MiddlewareFn extends anyThe type of the middleware function/handler

Constructors

Constructor

ts
new Runner<MiddlewareFn>(middleware: MiddlewareFn[]): Runner<MiddlewareFn>;

Parameters

ParameterType
middlewareMiddlewareFn[]

Returns

Runner<MiddlewareFn>

Methods

errorHandler()

ts
errorHandler(errorHandler: ErrorHandler): this;

Sets a custom error handler to catch exceptions in the middleware pipeline.

By default, exceptions raised in the middleware pipeline bubble up to the run method and can be captured using a try/catch block. When an exception is raised, the middleware downstream logic will not run.

Defining an error handler changes this behavior:

  • The run method will not throw exceptions
  • Errors are caught and passed to the error handler
  • Middleware upstream logic (after next) can still execute

Parameters

ParameterTypeDescription
errorHandlerErrorHandlerThe function to handle errors

Returns

this

The Runner instance for method chaining

Example

ts
await middleware
  .runner()
  .errorHandler((error) => {
    console.error("Middleware error:", error);
  })
  .run((fn, next) => fn(context, next));

finalHandler()

ts
finalHandler(finalHandler: FinalHandler): this;

Sets a custom final handler to execute when the middleware chain completes successfully.

The final handler is called when the entire middleware pipeline reaches the end by calling next through all handlers. This makes it easier to execute custom logic that is not part of the chain but must run when the chain ends.

Parameters

ParameterTypeDescription
finalHandlerFinalHandlerThe function to execute when the chain completes

Returns

this

The Runner instance for method chaining

Example

ts
await middleware
  .runner()
  .finalHandler(() => {
    console.log("All middleware completed");
  })
  .run((fn, next) => fn(context, next));

run()

ts
run(cb: Executor<MiddlewareFn>): Promise<void>;

Executes the middleware pipeline using the provided executor function.

The executor function is responsible for invoking each middleware handler with the appropriate context and the next callback. Since you control the executor, you can pass any data you want to the middleware.

Parameters

ParameterTypeDescription
cbExecutor<MiddlewareFn>The executor function that invokes each middleware handler

Returns

Promise<void>

A Promise that resolves when the middleware pipeline completes

Example

ts
const context = { user: null };
type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>;

const middleware = new Middleware<MiddlewareFn>();
middleware.add(async (ctx, next) => {
  ctx.user = await authenticate();
  await next();
});

await middleware.runner().run((fn, next) => fn(context, next));