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

# BlazeRequest: Accessing Params, Body, and CF Bindings

> BlazeRequest extends the Web Platform Request with Cloudflare bindings, route params, query helpers, body parsers, and metadata — all type-safe.

`BlazeRequest` wraps the standard Web Platform `Request` and adds everything you need to build a Cloudflare Workers API: typed `env` bindings, the `ExecutionContext`, route parameters, query helpers, cached body parsers, and CF-specific metadata. Blaze constructs one `BlazeRequest` instance per incoming request inside `app.fetch` and passes it through the entire middleware chain by reference — so any property you attach in one middleware is visible in every subsequent handler.

***

## Cloudflare-native properties

These properties give you direct, type-safe access to your Workers bindings and routing context.

| Property     | Type                     | Description                                                                                                                                                    |
| ------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `req.env`    | `Env`                    | Your typed Cloudflare bindings. The `Env` generic flows from `createApp<Env>()`, so every property is inferred automatically.                                  |
| `req.ctx`    | `ExecutionContext`       | The Workers execution context. Use `req.ctx.waitUntil()` for background tasks and `req.ctx.passThroughOnException()` to allow pass-through on uncaught errors. |
| `req.params` | `Record<string, string>` | Path parameters extracted by the TrieRouter. Populated only inside the matched route handler.                                                                  |
| `req.query`  | `URLSearchParams`        | Parsed query string. Use `.get()`, `.getAll()`, and `.has()`.                                                                                                  |

```ts theme={null}
app.get('/users/:id', async (req, res) => {
  // Typed KV and D1 access from req.env
  const cachedUser = await req.env.KV.get(`user:${req.params.id}`, { type: 'json' })
  if (cachedUser) return res.json(cachedUser)

  // req.params — populated by the trie match on '/users/:id'
  const user = await req.env.DB
    .prepare('SELECT * FROM users WHERE id = ?')
    .bind(req.params.id)
    .first()

  // req.ctx.waitUntil — runs after response is sent, does not block
  req.ctx.waitUntil(
    req.env.KV.put(`user:${req.params.id}`, JSON.stringify(user), {
      expirationTtl: 300,
    })
  )

  res.json(user)
})

app.get('/search', (req, res) => {
  // req.query — standard URLSearchParams
  const page  = Number(req.query.get('page') ?? 1)
  const limit = Number(req.query.get('limit') ?? 20)
  const tags  = req.query.getAll('tag')   // ?tag=ts&tag=cloudflare

  res.json({ page, limit, tags })
})
```

***

## Body helpers

Blaze reads the raw request body once into an internal `ArrayBuffer` cache. Every body helper below re-uses that cache, so you can safely call `req.json()` or `req.text()` multiple times within the same request — including from different middleware functions — without draining the stream.

| Method              | Return type            | Description                                                              |
| ------------------- | ---------------------- | ------------------------------------------------------------------------ |
| `req.json<T>()`     | `Promise<T>`           | Parses an `application/json` body. Cached — safe to call multiple times. |
| `req.text()`        | `Promise<string>`      | Returns the body decoded as UTF-8 text.                                  |
| `req.formData()`    | `Promise<FormData>`    | Parses `multipart/form-data` or `application/x-www-form-urlencoded`.     |
| `req.arrayBuffer()` | `Promise<ArrayBuffer>` | Returns the raw body bytes.                                              |
| `req.blob()`        | `Promise<Blob>`        | Returns a `Blob` with the `Content-Type` from the request headers.       |

<CodeGroup>
  ```ts JSON body theme={null}
  app.post('/articles', async (req, res) => {
    // Type-safe JSON parse — T inferred or explicitly provided
    const { title, body, tags } = await req.json<{
      title: string
      body: string
      tags: string[]
    }>()

    const result = await req.env.DB
      .prepare('INSERT INTO articles (title, body) VALUES (?, ?) RETURNING id')
      .bind(title, body)
      .first()

    res.status(201).json({ id: result?.id })
  })
  ```

  ```ts Form data theme={null}
  app.post('/upload', async (req, res) => {
    const form = await req.formData()
    const file = form.get('avatar') as File

    await req.env.R2.put(`avatars/${req.params.id}`, await file.arrayBuffer(), {
      httpMetadata: { contentType: file.type },
    })

    res.json({ ok: true })
  })
  ```

  ```ts Raw buffer theme={null}
  app.put('/files/:key', async (req, res) => {
    const buffer = await req.arrayBuffer()

    await req.env.R2.put(req.params.key, buffer, {
      httpMetadata: {
        contentType: req.header('Content-Type') ?? 'application/octet-stream',
      },
    })

    res.status(201).json({ key: req.params.key })
  })
  ```
</CodeGroup>

<Note>
  `req.json()` caches the raw `ArrayBuffer`, then decodes and parses it on
  each call. If you need to pass the body to another function that expects a
  stream, use `req.arrayBuffer()` instead and construct the stream yourself.
</Note>

***

## Metadata helpers

Use these to inspect the client, content type, and request headers without reaching into the raw `Headers` object.

| Property / Method    | Return type                                | Description                                                                                                                                                  |
| -------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `req.ip`             | `string`                                   | Client IP address, read from `CF-Connecting-IP` (or `X-Forwarded-For` as fallback).                                                                          |
| `req.cf`             | `IncomingRequestCfProperties \| undefined` | Cloudflare metadata: country, datacenter, colo, TLS version, ASN, and more. Only populated on the Workers runtime.                                           |
| `req.header(name)`   | `string \| null`                           | Case-insensitive header lookup.                                                                                                                              |
| `req.accepts(types)` | `string \| false`                          | Content negotiation. Returns the best matching type from `types` against the `Accept` header, or `false`. Supports shorthands: `'json'`, `'html'`, `'text'`. |
| `req.is(type)`       | `boolean`                                  | Checks `Content-Type`. Supports shorthands: `'json'`, `'form'`, `'text'`, `'html'`, `'xml'`.                                                                 |

```ts theme={null}
app.post('/ingest', async (req, res) => {
  // Block requests from outside the expected country
  if (req.cf?.country !== 'US') {
    return res.status(403).json({ error: 'Region not supported' })
  }

  // Log the client IP
  console.log(`Request from ${req.ip} via ${req.cf?.colo}`)

  // Reject unsupported content types early
  if (!req.is('json')) {
    return res.status(415).json({ error: 'Expected application/json' })
  }

  // Respond in the format the client prefers
  const data = await req.json()
  const format = req.accepts(['json', 'text'])

  if (format === 'text') {
    return res.send(JSON.stringify(data, null, 2))
  }

  res.json(data)
})
```

***

## TypeScript augmentation

Middleware often attaches custom properties to `req` (e.g. `req.user` from an auth middleware, `req.id` from a request-ID middleware). Declare these additions by augmenting the `BlazeRequest` interface in a `.d.ts` file — TypeScript will then infer them everywhere `req` appears.

```ts types/blaze.d.ts theme={null}
declare module 'blaze' {
  interface BlazeRequest {
    /** Attached by requireAuth middleware */
    user?: {
      id: string
      email: string
      isAdmin: boolean
    }
    /** Attached by requestId middleware */
    id: string
    /** Attached by requestTimer middleware */
    startTime: number
  }
}
```

```ts middleware/auth.ts theme={null}
import type { Handler } from 'blaze'

export const requireAuth: Handler<Env> = async (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '')
  if (!token) return res.status(401).json({ error: 'Unauthorized' })

  try {
    req.user = await verifyJwt(token, req.env.JWT_SECRET)
    next()
  } catch {
    next(new BlazeError(401, 'Invalid token'))
  }
}
```

***

## Complete handler example

This example uses several `req` properties together to demonstrate a realistic route:

```ts theme={null}
app.get('/dashboard', async (req, res) => {
  // 1. Metadata — geo-gate the endpoint
  const country = req.cf?.country ?? 'unknown'
  if (country === 'XX') {
    return res.status(451).json({ error: 'Unavailable in your region' })
  }

  // 2. Auth — user attached by requireAuth middleware upstream
  if (!req.user?.isAdmin) {
    return res.status(403).json({ error: 'Admin access required' })
  }

  // 3. Query params — pagination
  const page  = Number(req.query.get('page') ?? 1)
  const limit = Number(req.query.get('limit') ?? 20)

  // 4. D1 query using typed env
  const { results } = await req.env.DB
    .prepare('SELECT * FROM events ORDER BY created_at DESC LIMIT ? OFFSET ?')
    .bind(limit, (page - 1) * limit)
    .all()

  // 5. Background analytics — doesn't block the response
  req.ctx.waitUntil(
    req.env.KV.put(
      `dashboard-last-viewed:${req.user.id}`,
      new Date().toISOString(),
    )
  )

  res.json({ page, results })
})
```
