Pipe
The pipe function combines multiple middleware into one. It takes an array of middleware and returns a new middleware that processes a request and context through each middleware in sequence.
Usage
Let's start with the following middlewares and handlers:
const middlewareStatus = (() => (request, context, runtime) => {
return { status: 'OK' };
}) satisfies Get<[], UniversalMiddleware>;
const middlewareEarlyResponseError = (() => (request, context, runtime) => {
if (context.status && context.status !== 'OK') {
return new Response("Error", { status: 500 });
}
}) satisfies Get<[], UniversalMiddleware<{ status?: string }>>;
const handler = (() => (request, context, runtime) => {
return new Response(context.status ?? "OK");
}) satisfies Get<[], UniversalHandler<{ status?: string }>>;You can pipe universal middlewares together, creating a new middleware that combines their behavior:
const newMiddleware = pipe(middlewareStatus(), middlewareEarlyResponseError())You can also pipe universal middlewares and handlers together, creating a new handler that combines their behavior:
const newHandler = pipe(middlewareStatus(), middlewareEarlyResponseError(), handler())The middlewares run as apply runs a list: sorted by order, and following runs. Once a middleware or handler answers with a Response, a middleware listed after it only runs if it has a positive order (or runs), and every response function applies to the answer, in order:
pipe(
handler(),
logRequest, // no order: doesn't run, the handler already answered
enhance(addHeader, { order: MiddlewareOrder.HEADER_MANAGEMENT }), // runs, its response function applies
)When nothing answers, the new middleware passes the request on with what its middlewares returned: their context, or a response function that applies all of theirs to the response that comes next. If both, the context goes into the context object it was given.
pipe ignores path and method. To route handlers and scope middlewares by them, use pipeRoute, which composes a list as apply does.
You can also pipe middlewares or handlers that are adapter specific.
Any adapter specific middleware will act as if they were universal:
import { createMiddleware } from "@universal-middleware/hono";
const honoMiddlewareStatus = createMiddleware(middlewareStatus);
const newHandler = pipe(honoMiddlewareStatus(), middlewareEarlyResponseError(), handler())
// ^ `newHandler` is a universal handler because `handler` is universalAny adapter specific handler will keep their signature once returned:
import { createHandler } from "@universal-middleware/hono";
const honoHandler = createHandler(handler);
const newHonoHandler = pipe(middlewareStatus(), middlewareEarlyResponseError(), honoHandler())
// ^ `newHonoHandler` is a hono handler because `honoHandler` is hono-specific
// It can directly be used by hono: app.get("/", newHonoHandler);WARNING
This is only valid for middlewares and handlers generated thanks to universal-middleware

