Skip to content

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:

ts
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:

ts
const 
newMiddleware
=
pipe
(
middlewareStatus
(),
middlewareEarlyResponseError
())

You can also pipe universal middlewares and handlers together, creating a new handler that combines their behavior:

ts
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:

ts
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:

ts
import { 
createMiddleware
} from "@universal-middleware/hono";
const
honoMiddlewareStatus
=
createMiddleware
(
middlewareStatus
);
const
newHandler
=
pipe
(
honoMiddlewareStatus
(),
middlewareEarlyResponseError
(),
handler
())
// ^ `newHandler` is a universal handler because `handler` is universal

Any adapter specific handler will keep their signature once returned:

ts
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