> ## Documentation Index
> Fetch the complete documentation index at: https://wemstudios.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Blaze Middleware: Built-in Modules and Custom Handlers

> Blaze ships 12 tree-shakeable middleware modules using the Express (req, res, next) convention. Any Express middleware works unchanged in Blaze.

Blaze's middleware system gives you a familiar Express-style `(req, res, next)` pipeline that runs inside a Cloudflare Worker. Every piece of middleware you've written for Express works unchanged in Blaze, and the 12 built-in modules cover the most common API needs — authentication, CORS, caching, rate limiting, and more — without adding a single npm dependency.

## How middleware works

When a request arrives, Blaze walks a **Layer stack** — an ordered array of middleware and route Layers registered at startup. Each Layer has a `path` (derived from the first argument of `app.use()`) and a `handle` function.

* `app.use(fn)` appends a global Layer that matches every path.
* `app.use('/prefix', fn)` appends a Layer that only matches paths starting with `/prefix`.
* Inside each Layer, calling `next()` hands control to the next matching Layer.
* If a Layer throws synchronously, or an async handler rejects, Blaze catches the error and routes it to the nearest **4-arg error handler**.

<Tip>
  Middleware registered earlier in your code runs first. Load global concerns — request IDs, loggers, CORS — before route-specific middleware and your route handlers.
</Tip>

```typescript theme={null}
import { createApp } from 'blaze'

const app = createApp<Env>()

app.use(requestId())   // ← runs first
app.use(logger())      // ← runs second
app.use('/api', auth)  // ← runs third, only for /api/*

app.get('/api/users', listUsers)

export default { fetch: app.fetch }
```

When a Layer calls `res.json()`, `res.send()`, or `res.html()`, it resolves the internal response promise and the cycle ends — downstream Layers are not called.

## Writing custom middleware

### Synchronous middleware

```typescript theme={null}
import type { BlazeRequest, BlazeResponse, NextFunction } from 'blaze'

function addRegion(req: BlazeRequest, res: BlazeResponse, next: NextFunction) {
  // req.cf is populated by Cloudflare with datacenter metadata
  res.header('X-Region', req.cf?.region ?? 'unknown')
  next()
}

app.use(addRegion)
```

### Async middleware

```typescript theme={null}
async function requirePlan(
  req: BlazeRequest,
  res: BlazeResponse,
  next: NextFunction,
) {
  const plan = await req.env.DB
    .prepare('SELECT plan FROM subscriptions WHERE user_id = ?')
    .bind(req.user?.sub)
    .first<{ plan: string }>()

  if (plan?.plan !== 'pro') {
    return res.status(403).json({ error: 'Pro plan required' })
  }

  next()
}

app.use('/api/export', requirePlan)
```

### Forwarding errors with `next(err)`

When something goes wrong inside middleware, pass the error to `next` instead of letting it propagate unhandled. Blaze routes it to the nearest downstream error handler.

```typescript theme={null}
async function loadUser(
  req: BlazeRequest,
  res: BlazeResponse,
  next: NextFunction,
) {
  try {
    req.user = await fetchUser(req.env.DB, req.params.id)
    next()
  } catch (err) {
    next(err) // ← forwards to the error handler below
  }
}
```

### Error handlers (4-argument signature)

An error handler is a middleware function that accepts **four** arguments: `(err, req, res, next)`. Blaze identifies it by arity and only invokes it when an error has been forwarded.

```typescript theme={null}
function errorHandler(
  err: unknown,
  req: BlazeRequest,
  res: BlazeResponse,
  next: NextFunction,
) {
  const status = (err as any)?.status ?? 500
  const message = err instanceof Error ? err.message : 'Internal Server Error'

  console.error(`[${req.id}] ${status} ${message}`)
  res.status(status).json({ error: message })
}

// Error handlers must be registered after all other middleware and routes
app.use(errorHandler)
```

## All built-in middleware

<CardGroup cols={2}>
  <Card title="cors" icon="globe">
    Set `Access-Control-*` headers and handle OPTIONS preflight. Supports dynamic origins, credentials, and `maxAge`.

    ```typescript theme={null}
    import { cors } from 'blaze/middleware/cors'
    ```
  </Card>

  <Card title="logger" icon="rectangle-terminal">
    Log method, path, status, response time, and Cloudflare colo to `console.log`. Supports short, long, and custom formats.

    ```typescript theme={null}
    import { logger } from 'blaze/middleware/logger'
    ```
  </Card>

  <Card title="bearerAuth" icon="key">
    Validate `Authorization: Bearer <token>` headers against a static token or an async validator function.

    ```typescript theme={null}
    import { bearerAuth } from 'blaze/middleware/bearer-auth'
    ```
  </Card>

  <Card title="basicAuth" icon="lock">
    Decode and verify `Authorization: Basic <base64>` credentials using timing-safe comparison.

    ```typescript theme={null}
    import { basicAuth } from 'blaze/middleware/basic-auth'
    ```
  </Card>

  <Card title="jwtAuth" icon="shield-halved">
    Verify JWTs using Web Crypto (`crypto.subtle`). Supports HS256 and RS256. Sets decoded payload on `req.user`.

    ```typescript theme={null}
    import { jwtAuth } from 'blaze/middleware/jwt'
    ```
  </Card>

  <Card title="rateLimit" icon="gauge">
    Sliding-window rate limiting backed by Cloudflare KV. Configurable per-route with custom key functions.

    ```typescript theme={null}
    import { rateLimit } from 'blaze/middleware/rate-limit'
    ```
  </Card>

  <Card title="cache" icon="database">
    Cache responses via the Cloudflare Cache API. Respects existing `Cache-Control` headers. Only caches GET by default.

    ```typescript theme={null}
    import { cache } from 'blaze/middleware/cache'
    ```
  </Card>

  <Card title="compress" icon="file-zipper">
    Compress response bodies with gzip or deflate via `CompressionStream`. Checks `Accept-Encoding` automatically.

    ```typescript theme={null}
    import { compress } from 'blaze/middleware/compress'
    ```
  </Card>

  <Card title="requestId" icon="fingerprint">
    Attach a UUID to `req.id` and echo it as the `X-Request-Id` response header. Reuses incoming ID if present.

    ```typescript theme={null}
    import { requestId } from 'blaze/middleware/request-id'
    ```
  </Card>

  <Card title="etag" icon="tag">
    Generate weak ETags from response bodies using a fast djb2 hash. Handles `If-None-Match` → 304 automatically.

    ```typescript theme={null}
    import { etag } from 'blaze/middleware/etag'
    ```
  </Card>

  <Card title="timeout" icon="clock">
    Race the downstream handler chain against a timer. Calls `next(err)` with a 408 error if the timer fires first.

    ```typescript theme={null}
    import { timeout } from 'blaze/middleware/timeout'
    ```
  </Card>

  <Card title="secureHdrs" icon="shield">
    Set CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy, and CORP/COEP/COOP on every response.

    ```typescript theme={null}
    import { secureHdrs } from 'blaze/middleware/secure-headers'
    ```
  </Card>
</CardGroup>

## Middleware scope

You control exactly which requests a middleware runs for by where and how you register it.

### Global — every request

```typescript theme={null}
app.use(requestId())
app.use(logger())
app.use(secureHdrs())
```

### Path-scoped — a URL prefix only

```typescript theme={null}
// Only runs for requests to /admin/*
app.use(
  '/admin',
  basicAuth({
    username: (req) => req.env.ADMIN_USER,
    password: (req) => req.env.ADMIN_PASS,
  }),
)
```

### Route-level inline stack

Pass middleware directly as extra arguments to any route method. They run only for that route, in order, before the final handler.

```typescript theme={null}
app.post(
  '/upload',
  authenticate,          // ← middleware 1
  validateUploadBody,    // ← middleware 2
  handleUpload,          // ← final handler
)
```

### Sub-router scope

Mount a Router as middleware to apply a shared stack to a group of routes without polluting the global Layer stack.

```typescript theme={null}
const api = app.Router()

api.use(jwtAuth({ secret: (req) => req.env.JWT_SECRET }))
api.use(rateLimit({ kvBinding: (req) => req.env.RATE_KV, limit: 100, window: 60 }))

api.get('/users', listUsers)
api.post('/users', createUser)

app.use('/api/v1', api)
```
