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

# Using Cloudflare KV Namespaces in Blaze via req.env

> Access Cloudflare KV namespaces directly via req.env in any Blaze handler. Read, write, list, and delete key-value pairs with full TypeScript types.

Blaze injects every KV namespace declared in your `wrangler.toml` directly onto `req.env` with full type safety. Once you add the binding to your `Env` type and pass it to `createApp<Env>()`, every handler in your application can read, write, list, and delete keys — no extra setup, no manual passing of the namespace, no casting.

## Configure the binding

Start by declaring the namespace in `wrangler.toml`, then mirror it in your `Env` type.

<Steps>
  <Step title="Add the namespace to wrangler.toml">
    ```toml wrangler.toml theme={null}
    [[kv_namespaces]]
    binding = "CACHE"
    id      = "abc123def456abc123def456abc123de"
    ```
  </Step>

  <Step title="Define the Env type">
    ```typescript src/types/env.ts theme={null}
    export type Env = {
      CACHE: KVNamespace;
      // ...other bindings
    };
    ```
  </Step>

  <Step title="Pass Env to createApp">
    ```typescript src/index.ts theme={null}
    import { createApp } from "blaze";
    import type { Env } from "./types/env";

    const app = createApp<Env>();
    // req.env.CACHE is now fully typed as KVNamespace in every handler
    ```
  </Step>
</Steps>

## Reading from KV

Use `req.env.CACHE.get(key, { type: 'json' })` to retrieve a stored value and let Blaze handle deserialization. The method returns `null` when the key does not exist, so always guard against a missing value before responding.

```typescript theme={null}
app.get("/cache/:key", async (req, res) => {
  const value = await req.env.CACHE.get(req.params.key, { type: "json" });

  if (value === null) {
    return res.status(404).json({ error: "Key not found" });
  }

  res.json(value);
});
```

The `type: 'json'` option tells the KV runtime to deserialize the stored string automatically. Other supported types are `'text'` (default), `'arrayBuffer'`, and `'stream'`.

## Writing to KV

Use `req.env.CACHE.put(key, value, options)` to store a value. Pass `expirationTtl` (seconds from now) or `expiration` (Unix timestamp) to set a TTL — keys without a TTL persist indefinitely.

```typescript theme={null}
app.put("/cache/:key", async (req, res) => {
  const body = await req.json<Record<string, unknown>>();

  await req.env.CACHE.put(
    req.params.key,
    JSON.stringify(body),
    { expirationTtl: 3600 } // expires in 1 hour
  );

  res.status(201).json({ ok: true, key: req.params.key });
});
```

<Note>
  KV `put` operations are **strongly consistent** within the same Cloudflare
  datacenter but **eventually consistent** globally. Expect propagation delays
  of up to 60 seconds across regions.
</Note>

## Listing keys

`req.env.CACHE.list()` returns up to 1,000 keys at a time. Use the `prefix` option to scope the listing, and check `list_complete` to detect when there are more pages to fetch.

```typescript theme={null}
app.get("/cache", async (req, res) => {
  const prefix = req.query.get("prefix") ?? undefined;

  const { keys, list_complete, cursor } = await req.env.CACHE.list({
    prefix,
    limit: 100,
  });

  res.json({
    keys: keys.map((k) => ({ name: k.name, expiration: k.expiration })),
    list_complete,
    cursor: list_complete ? undefined : cursor,
  });
});
```

## Deleting keys

`req.env.CACHE.delete(key)` removes a key immediately. Deleting a key that does not exist is a no-op — no error is thrown.

```typescript theme={null}
app.delete("/cache/:key", async (req, res) => {
  await req.env.CACHE.delete(req.params.key);
  res.status(204).send();
});
```

## Fire-and-forget writes

When a KV write does not need to complete before you send a response, hand the promise to `req.ctx.waitUntil()`. Blaze will keep the Worker alive to finish the write without blocking the response returned to the client.

```typescript theme={null}
app.post("/events", async (req, res) => {
  const event = await req.json<{ type: string; data: unknown }>();

  // Respond immediately — the write happens in the background
  res.status(202).json({ status: "accepted" });

  req.ctx.waitUntil(
    req.env.CACHE.put(
      `event:${crypto.randomUUID()}`,
      JSON.stringify(event),
      { expirationTtl: 86400 }
    )
  );
});
```

<Tip>
  KV is an excellent store for **configuration values and feature flags** that
  change infrequently. Because Cloudflare isolates can persist across multiple
  requests within the same datacenter, you can read a flag once per isolate
  startup and cache it in a module-level variable — dramatically reducing KV
  read costs for high-traffic workers.

  ```typescript theme={null}
  // Module-level cache — populated once per isolate
  let featureFlags: Record<string, boolean> | null = null;

  app.use(async (req, _res, next) => {
    if (!featureFlags) {
      featureFlags =
        await req.env.CACHE.get<Record<string, boolean>>("feature-flags", {
          type: "json",
        }) ?? {};
    }
    next();
  });
  ```
</Tip>
