> ## 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.

# Utility Middleware: Logger, Request ID, Timeout, and Headers

> Blaze utility middleware for logging, request IDs, timeouts, and security headers — all operational concerns for production Cloudflare Workers APIs.

Blaze's four utility middleware modules cover the operational concerns that every production API needs: structured request logging, stable request identifiers for tracing, hard limits on handler duration, and security headers that browsers and scanners expect. Each module is a separate tree-shakeable import so you only bundle what you use.

## Logger

`logger()` logs each request's method, path, HTTP status, and response time to `console.log`. In the short format (default) it appends the **Cloudflare colo code** — the three-letter identifier of the datacenter that handled the request — making it easy to spot latency hotspots across regions.

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

### `LoggerOptions`

| Option   | Type                                                       | Default   | Description                                                                                                                                              |
| -------- | ---------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `format` | `'short' \| 'long' \| (req, status, durationMs) => string` | `'short'` | Log format. `'short'` logs method, path, status, duration, and colo. `'long'` adds client IP and request ID. Pass a function to build your own log line. |

<CodeGroup>
  ```typescript Short format (default) theme={null}
  import { logger } from 'blaze/middleware/logger'

  app.use(logger())

  // Produces:
  // GET /api/products 200 12ms [AMS]
  // POST /api/orders 201 34ms [LAX]
  ```

  ```typescript Long format theme={null}
  import { logger } from 'blaze/middleware/logger'

  app.use(logger({ format: 'long' }))

  // Produces:
  // GET /api/products 200 12ms ip=1.2.3.4 colo=AMS id=c3b1a4f2-...
  ```

  ```typescript Custom formatter theme={null}
  import { logger } from 'blaze/middleware/logger'

  app.use(
    logger({
      format: (req, status, duration) => {
        // Emit structured JSON for ingestion by a log aggregator
        return JSON.stringify({
          method: req.method,
          path: req.url,
          status,
          duration,
          colo: req.cf?.colo,
          requestId: req.id,
          country: req.cf?.country,
        })
      },
    }),
  )
  ```
</CodeGroup>

`logger()` hooks into `res.onSend()` to capture the final status code and response time after your route handler completes — it adds no measurable latency to the request path.

## Request ID

`requestId()` generates a UUID for each request using `crypto.randomUUID()`, attaches it to `req.id`, and sets it as the `X-Request-Id` response header. If the incoming request already carries an `X-Request-Id` header (e.g. forwarded from an upstream gateway), `requestId()` reuses that value rather than generating a new one — maintaining end-to-end trace continuity.

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

### `RequestIdOptions`

| Option       | Type           | Default               | Description                                                                           |
| ------------ | -------------- | --------------------- | ------------------------------------------------------------------------------------- |
| `headerName` | `string`       | `'X-Request-Id'`      | The request and response header name to read from and write to.                       |
| `generator`  | `() => string` | `crypto.randomUUID()` | Custom ID generator. Replace with a shorter or correlation-friendly format if needed. |

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

app.use(requestId())

app.get('/api/data', (req, res) => {
  // req.id is now the UUID — use it in error logs, downstream calls, etc.
  console.log(`[${req.id}] Handling request`)
  res.json({ requestId: req.id })
})
```

Register `requestId()` before `logger()` so the request ID is available in log output:

```typescript theme={null}
app.use(requestId())  // ← sets req.id
app.use(logger({ format: 'long' })) // ← reads req.id for log line
```

## Timeout

`timeout()` races the downstream handler chain against a timer. If the timer fires before your handler calls `next()` or sends a response, it calls `next(new BlazeError(408, 'Request Timeout'))` — routing to your error handler and returning `408 Request Timeout` to the client. If the handler completes first, the timer is cancelled automatically.

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

### `TimeoutOptions`

| Option     | Type     | Default             | Description                                |
| ---------- | -------- | ------------------- | ------------------------------------------ |
| `duration` | `number` | *(required)*        | Timeout in milliseconds.                   |
| `message`  | `string` | `'Request Timeout'` | Error message in the 408 response.         |
| `status`   | `number` | `408`               | HTTP status code for the timeout response. |

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

const app = createApp<Env>()

// Enforce a 5-second limit on all requests
app.use(timeout({ duration: 5000 }))

app.get('/api/slow', async (req, res) => {
  // If this takes more than 5 seconds, the client receives 408
  const result = await req.env.DB.prepare('SELECT * FROM large_table').all()
  res.json(result.results)
})

// Tighter timeout for real-time endpoints
app.get('/api/live', timeout({ duration: 1000 }), handleLiveData)
```

<Note>
  Cloudflare Workers already enforce a maximum CPU time per request (typically 50 ms on the free plan and up to 30 seconds on paid plans). `timeout()` is useful for keeping your own SLO well inside Cloudflare's hard limit — for example, failing fast at 5 seconds on a plan with a 30-second wall clock limit so clients see a clean error instead of a Cloudflare edge timeout.
</Note>

## Secure headers

`secureHdrs()` sets a collection of security response headers that protect against common web vulnerabilities. All headers are pre-computed at middleware registration time (not on every request), so the per-request overhead is just a handful of `Map` lookups.

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

### Default headers

Out of the box, `secureHdrs()` sets:

| Header                      | Default value                              |
| --------------------------- | ------------------------------------------ |
| `Content-Security-Policy`   | `default-src 'self'`                       |
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains`      |
| `X-Frame-Options`           | `DENY`                                     |
| `X-Content-Type-Options`    | `nosniff`                                  |
| `Referrer-Policy`           | `strict-origin-when-cross-origin`          |
| `X-XSS-Protection`          | `0` *(disabled — CSP is preferred)*        |
| `Permissions-Policy`        | `geolocation=(), camera=(), microphone=()` |

### `SecureHeadersOptions`

Each header can be overridden with a custom string or disabled entirely by passing `false`. Options that are omitted use the default value from the table above; options with no listed default are not set unless you provide a value.

| Option                      | Maps to header                 | Default                                      |
| --------------------------- | ------------------------------ | -------------------------------------------- |
| `contentSecurityPolicy`     | `Content-Security-Policy`      | `"default-src 'self'"`                       |
| `strictTransportSecurity`   | `Strict-Transport-Security`    | `"max-age=31536000; includeSubDomains"`      |
| `xFrameOptions`             | `X-Frame-Options`              | `"DENY"`                                     |
| `xContentTypeOptions`       | `X-Content-Type-Options`       | `"nosniff"`                                  |
| `referrerPolicy`            | `Referrer-Policy`              | `"strict-origin-when-cross-origin"`          |
| `xXssProtection`            | `X-XSS-Protection`             | `"0"`                                        |
| `permissionsPolicy`         | `Permissions-Policy`           | `"geolocation=(), camera=(), microphone=()"` |
| `crossOriginEmbedderPolicy` | `Cross-Origin-Embedder-Policy` | *(not set)*                                  |
| `crossOriginOpenerPolicy`   | `Cross-Origin-Opener-Policy`   | *(not set)*                                  |
| `crossOriginResourcePolicy` | `Cross-Origin-Resource-Policy` | *(not set)*                                  |

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

app.use(
  secureHdrs({
    // Tighten CSP for an API that serves no HTML
    contentSecurityPolicy: "default-src 'none'",

    // Keep HSTS — domain already has HTTPS everywhere
    strictTransportSecurity: 'max-age=63072000; includeSubDomains; preload',

    // API responses are never framed — keep the default DENY
    // xFrameOptions: 'DENY',   (omitting uses the default)

    // Disable Permissions-Policy for a microservice with no browser clients
    permissionsPolicy: false,

    // Enable COEP/COOP/CORP for cross-origin isolation (e.g. SharedArrayBuffer)
    crossOriginEmbedderPolicy: 'require-corp',
    crossOriginOpenerPolicy: 'same-origin',
    crossOriginResourcePolicy: 'same-origin',
  }),
)
```

## Recommended global stack

Combine all four utilities at the top of your app for a production-ready baseline:

```typescript theme={null}
import { createApp } from 'blaze'
import { requestId } from 'blaze/middleware/request-id'
import { logger } from 'blaze/middleware/logger'
import { secureHdrs } from 'blaze/middleware/secure-headers'
import { timeout } from 'blaze/middleware/timeout'
import type { Env } from './types/env'

const app = createApp<Env>()

// 1. Assign a stable ID first — logger and error handlers can read it.
app.use(requestId())

// 2. Log every request with method, path, status, timing, and colo.
app.use(logger())

// 3. Apply security headers to every response.
app.use(secureHdrs())

// 4. Enforce a hard ceiling on handler duration.
app.use(timeout({ duration: 10_000 }))

// — mount your routes below this line —
app.use('/api', apiRouter)

// 5. Global error handler — uses req.id set by requestId().
app.onError((err, req, res, _next) => {
  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, requestId: req.id })
})

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