---
title: "Middleware Composition"
description: "Middleware composition in Hono is a powerful architectural pattern that allows developers to chain independent request handlers into a unified pipeline. This mechanism enables cross-cutting concern..."
last_updated: "2026-07-02T09:13:46.828832+00:00"
canonical_url: "https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/middleware-composition"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [src/types.ts](https://github.com/blade47/hono/blob/main/src/types.ts)
- [src/middleware/secure-headers/secure-headers.ts](https://github.com/blade47/hono/blob/main/src/middleware/secure-headers/secure-headers.ts)
- [src/hono-base.ts](https://github.com/blade47/hono/blob/main/src/hono-base.ts)
- [src/adapter/aws-lambda/handler.ts](https://github.com/blade47/hono/blob/main/src/adapter/aws-lambda/handler.ts)
- [src/middleware/cache/index.ts](https://github.com/blade47/hono/blob/main/src/middleware/cache/index.ts)
- [src/middleware/compress/index.ts](https://github.com/blade47/hono/blob/main/src/middleware/compress/index.ts)
- [src/adapter/cloudflare-pages/handler.ts](https://github.com/blade47/hono/blob/main/src/adapter/cloudflare-pages/handler.ts)
- [src/middleware/combine/index.ts](https://github.com/blade47/hono/blob/main/src/middleware/combine/index.ts)
</details>

Middleware composition in Hono is a powerful architectural pattern that allows developers to chain independent request handlers into a unified pipeline. This mechanism enables cross-cutting concerns—such as security, caching, and compression—to be applied uniformly across routes or specific path patterns without polluting individual business-logic handlers.

At its core, composition follows a "decorator-like" pattern, where each middleware has the power to pre-process requests, execute subsequent handlers via a control-flow callback function, and post-process responses. This flow allows Hono to build complex stacks of behavior that are both modular and testable, keeping the application's request-response lifecycle clean.

The subsystem serves as the glue for Hono's extensibility. By leveraging the control-flow callback pattern, composition facilitates fine-grained control over execution flow. Developers can conditionally skip middleware, intercept early responses, or perform teardown logic after other handlers have completed. This design choice is fundamental to Hono's performance, as it avoids unnecessary overhead by resolving the execution order statically during router initialization and dynamically via the `compose` pipeline during runtime dispatch.

## Pipeline Dispatch and Control Flow
Hono employs a composition pipeline to sequence handlers for a matched route. The primary flow involves receiving a `Request`, routing it to identify a set of handlers, and then executing them sequentially. Each handler receives a `Context` object and a control-flow callback function. If a handler calls this function, control is passed to the next handler in the chain.

The mechanism relies on `compose`, which wraps an array of handlers. If a request matches multiple routes, the handlers are collapsed into a single execution stream. Crucially, if a handler does not call the provided callback and provides a response, the pipeline halts, and the final response is returned to the client, effectively short-circuiting the remaining middleware.

Sources: [src/hono-base.ts:406-466](https://github.com/blade47/hono/blob/main/src/hono-base.ts#L406-L466)

## Composable Middleware Strategies
The `combine` module provides higher-order composition primitives: `some`, `every`, and `except`. These functions act as logic gates for middleware execution.

*   `some`: Executes a list of handlers until one completes successfully. It short-circuits execution if a handler reports success (typically by returning `true`).
*   `every`: Requires all handlers to execute successfully. If any handler fails, the chain terminates.
*   `except`: Conditional execution where middleware is applied unless the `condition` (either a path string or a function) is met.

These primitives allow for sophisticated logic such as "skip authentication if the request comes from a local network" or "only run rate-limiting if the user lacks a valid bearer token."

```mermaid
flowchart TD
    A[Start] --> B{"some() logic"}
    B -->|Check Handler 1| C{Passed?}
    C -->|Yes| D[Execute callback]
    C -->|No| E[Check Handler 2]
    E --> F{Passed?}
    F -->|Yes| D
    F -->|No| G[Throw Error]
```
Sources: [src/middleware/combine/index.ts:38-164](https://github.com/blade47/hono/blob/main/src/middleware/combine/index.ts#L38-L164)

## Content-Driven Pipeline Transformations
Some middleware, like `compress`, act as stream-processing decorators. In this mechanism, the middleware calls the control-flow callback to allow the application logic to build a response, then captures that response to apply a `CompressionStream`.

This "wrap and transform" pattern is essential for modifying response bodies on the fly. The middleware must perform careful checks: it verifies that the `Content-Type` is compressible, checks for `no-transform` instructions in the `Cache-Control` header, and ensures that the response is not already compressed.

> [!IMPORTANT]
> The `compress` middleware converts strong ETags to weak ETags because the compression process alters the exact byte representation of the response body. This is a critical invariant to maintain cache consistency.

Sources: [src/middleware/compress/index.ts:86-121](https://github.com/blade47/hono/blob/main/src/middleware/compress/index.ts#L86-L121)

## Context-Aware Injection
Middleware composition frequently uses the `Context` to share state across the pipeline. For example, the `secure-headers` middleware provides a `NONCE` generator. This function checks the context for an existing nonce; if not found, it generates a new one and stores it via `ctx.set()`. This state is then accessible to downstream view engines or template renderers.

The `secureHeaders` middleware also evaluates callbacks before calling the control-flow callback, ensuring that any dependencies required by the response (like a nonce) are populated before the main handler runs, yet the final header injection happens after the main handler produces the response.

Sources: [src/middleware/secure-headers/secure-headers.ts:137-228](https://github.com/blade47/hono/blob/main/src/middleware/secure-headers/secure-headers.ts#L137-L228)

## Lifecycle Management: Cache Middleware
The `cache` middleware demonstrates a hybrid approach where it acts both as a guard and a pipeline modifier. It attempts a match against the Cache API before calling the control-flow callback. If a match exists, it terminates the request lifecycle early by returning the cached response.

If no match exists, it proceeds with the control-flow callback, waits for the application logic to populate the response, and then uses `c.executionCtx.waitUntil()` to asynchronously cache the response in the background. This ensures the request completion is not blocked by cache insertion.

```mermaid
sequenceDiagram
    participant Req as Request
    participant Mid as Cache Middleware
    participant App as Application Handler
    participant C as Cache API
    Req->>Mid: Process
    Mid->>C: match(key)
    alt Cached Response Found
        Mid-->>Req: Return Cached Response
    else No Hit
        Mid->>App: await callback()
        App-->>Mid: Return New Response
        Mid->>C: put(key, res)
        Mid-->>Req: Return Response
    end
```
Sources: [src/middleware/cache/index.ts:138-180](https://github.com/blade47/hono/blob/main/src/middleware/cache/index.ts#L138-L180)

## Performance and Selection Logics
When multiple middlewares or handlers are present, Hono manages execution priority. The router stores routes, and during dispatch, the matched route handlers are collected. The `compose` function ensures they are executed in the order of registration.

One critical safety guard exists in composition: `isNextCalled` in the `some()` implementation of the `combine` middleware. This variable prevents multiple invocations of the downstream pipeline, guaranteeing that if a condition is satisfied, the request lifecycle invariants are maintained.

> [!TIP]
> Use `combine` primitives for declarative, clean composition rather than manually tracking state across multiple `app.use()` registrations.

Sources: [src/middleware/combine/index.ts:39-68](https://github.com/blade47/hono/blob/main/src/middleware/combine/index.ts#L39-L68)

## Worked Example: Declarative Security Pipeline
This example demonstrates combining multiple security middlewares to protect an API endpoint.

```typescript
import { Hono } from 'hono'
import { secureHeaders, NONCE } from 'hono/secure-headers'
import { some } from 'hono/combine'
import { bearerAuth } from 'hono/bearer-auth'

const app = new Hono()

// Apply security headers with a custom CSP nonce
app.use(secureHeaders({
  contentSecurityPolicy: {
    scriptSrc: [NONCE],
  }
}))

// Use combined logic: skip authentication for specific paths
app.use('/api/*', some(
  (c) => c.req.path.startsWith('/api/public'),
  bearerAuth({ token: 'secret-token' })
))

app.get('/api/data', (c) => c.json({ data: 'secure' }))
```
Sources: [src/middleware/secure-headers/secure-headers.ts:184-190](https://github.com/blade47/hono/blob/main/src/middleware/secure-headers/secure-headers.ts#L184-L190), [src/middleware/combine/index.ts:38-68](https://github.com/blade47/hono/blob/main/src/middleware/combine/index.ts#L38-L68)

## Related

- [Context Execution](https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/context-execution)


## Sitemap

See the full [sitemap](https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/llms.txt) for all pages in this wiki.
