zudo-cloudflare-wisdom

Type to search...

to open search from anywhere

KV (Key-Value)

CreatedApr 4, 2026Takeshi Takatsudo

Cloudflare KV namespace usage patterns

Overview

KV is a global, low-latency key-value store. It is eventually consistent — writes propagate globally within ~60 seconds, but reads may return stale data during that window.

Setup

Create a Namespace

npx wrangler kv namespace create "MY_KV"

This outputs the namespace ID. Add it to wrangler.toml:

[[kv_namespaces]]
binding = "MY_KV"
id = "abc123def456ghi789"

Usage in Functions

interface Env {
  MY_KV: KVNamespace;
}

// Read
const value = await env.MY_KV.get("key");
const json = await env.MY_KV.get("key", { type: "json" });

// Write
await env.MY_KV.put("key", "value");
await env.MY_KV.put("key", JSON.stringify(data));

// Write with expiration (TTL in seconds)
await env.MY_KV.put("key", "value", { expirationTtl: 3600 });

// Delete
await env.MY_KV.delete("key");

// List keys
const list = await env.MY_KV.list({ prefix: "logs:" });

Real-World Pattern: Keyword Logs

From our zpaper project — logging search keywords to KV:

interface Env {
  KEYWORD_LOGS: KVNamespace;
}

export const onRequestGet: PagesFunction<Env> = async (context) => {
  const url = new URL(context.request.url);
  const query = url.searchParams.get("q")?.trim();

  if (query) {
    // Log the keyword asynchronously (don't block the response)
    const key = `search:${Date.now()}:${crypto.randomUUID()}`;
    context.waitUntil(
      context.env.KEYWORD_LOGS.put(key, JSON.stringify({
        query,
        timestamp: new Date().toISOString(),
      }), { expirationTtl: 86400 * 30 }) // 30 days
    );
  }

  // ... return search results
};

💡 Use waitUntil for Non-Critical Writes

context.waitUntil() lets you perform async work after the response is sent. Use it for logging, analytics, and other non-critical writes.

Gotchas

  • Eventually consistent: Reads immediately after writes may return stale data
  • 512-byte key limit: Keys cannot exceed 512 bytes
  • 25 MiB value limit: Values cannot exceed 25 MiB
  • List pagination: list() returns up to 1000 keys per call; use the cursor for pagination