cloudflare-kv

Cloudflare Workers KV

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cloudflare-kv" with this command: npx skills add itechmeat/llm-code/itechmeat-llm-code-cloudflare-kv

Cloudflare Workers KV

KV is a global, low-latency, eventually-consistent key-value store. Optimized for high-read, low-write workloads.

Quick Start

Create namespace

npx wrangler kv namespace create MY_KV

Add binding

// wrangler.jsonc { "kv_namespaces": [ { "binding": "MY_KV", "id": "06779da6940b431db6e566b4846d64db" } ] }

Basic Worker

export interface Env { MY_KV: KVNamespace; }

export default { async fetch(request: Request, env: Env): Promise<Response> { await env.MY_KV.put("user:123", JSON.stringify({ name: "Alice" })); const user = await env.MY_KV.get("user:123", "json"); const keys = await env.MY_KV.list({ prefix: "user:" }); await env.MY_KV.delete("user:123"); return Response.json({ user, keys: keys.keys }); }, };

Binding Configuration

// wrangler.jsonc { "kv_namespaces": [ { "binding": "MY_KV", // Variable name in env "id": "namespace-uuid", // Namespace ID "preview_id": "preview-uuid" // Optional: for local dev } ] }

wrangler.toml

[[kv_namespaces]] binding = "MY_KV" id = "namespace-uuid" preview_id = "preview-uuid"

Remote binding (use production in dev)

{ "kv_namespaces": [ { "binding": "MY_KV", "id": "namespace-uuid", "remote": true } ] }

See binding.md for details.

Workers API

put(key, value, options?)

await env.MY_KV.put("key", "value");

// With options await env.MY_KV.put("key", "value", { expirationTtl: 3600, // Expires in 1 hour metadata: { version: 1 }, });

// With absolute expiration await env.MY_KV.put("key", "value", { expiration: Math.floor(Date.now() / 1000) + 86400, // Unix timestamp });

Value types: string | ReadableStream | ArrayBuffer

Options:

Option Type Description

expirationTtl

number Seconds from now (min 60)

expiration

number Unix timestamp (seconds)

metadata

object JSON-serializable (max 1024 bytes)

get(key, type?)

// Text (default) const text = await env.MY_KV.get("key");

// JSON const obj = await env.MY_KV.get("key", "json");

// ArrayBuffer const buffer = await env.MY_KV.get("key", "arrayBuffer");

// Stream const stream = await env.MY_KV.get("key", "stream");

// With cache TTL const cached = await env.MY_KV.get("key", { type: "json", cacheTtl: 300 });

Returns null if key doesn't exist.

get(keys[]) — Multi-key read

const results = await env.MY_KV.get(["key1", "key2", "key3"], "json"); // Returns Map<string, T | null>

for (const [key, value] of results) { console.log(key, value); }

Maximum 100 keys per call.

getWithMetadata(key, type?)

const { value, metadata } = await env.MY_KV.getWithMetadata("key", "json"); // value: T | null // metadata: object | null

list(options?)

const result = await env.MY_KV.list(); // { keys: [...], list_complete: boolean, cursor?: string }

// With prefix const users = await env.MY_KV.list({ prefix: "user:" });

// Pagination let cursor: string | undefined; do { const result = await env.MY_KV.list({ cursor, limit: 100 }); for (const key of result.keys) { console.log(key.name, key.expiration, key.metadata); } cursor = result.list_complete ? undefined : result.cursor; } while (cursor);

Options:

Option Type Description

prefix

string Filter keys by prefix

limit

number Max keys (default/max: 1000)

cursor

string Pagination cursor

delete(key)

await env.MY_KV.delete("key"); // Resolves even if key doesn't exist

See api.md for complete reference.

Expiration

Relative (TTL)

await env.MY_KV.put("session:abc", token, { expirationTtl: 3600, // 1 hour from now });

Absolute

const expires = new Date("2025-01-01").getTime() / 1000; await env.MY_KV.put("promo:holiday", data, { expiration: expires, });

Minimum TTL: 60 seconds.

Expired keys are automatically deleted and not billed.

Metadata

Store up to 1024 bytes of JSON metadata per key.

// Write with metadata await env.MY_KV.put("file:123", fileContent, { metadata: { filename: "document.pdf", contentType: "application/pdf", uploadedBy: "user-456", }, });

// Read with metadata const { value, metadata } = await env.MY_KV.getWithMetadata("file:123", "stream");

// Metadata in list results const { keys } = await env.MY_KV.list({ prefix: "file:" }); for (const key of keys) { console.log(key.name, key.metadata); }

Pattern: Store small values directly in metadata:

await env.MY_KV.put("config:theme", "", { metadata: { value: "dark", version: 2 }, });

// Read via list without get() const { keys } = await env.MY_KV.list({ prefix: "config:" }); const theme = keys.find((k) => k.name === "config:theme")?.metadata?.value;

Cache TTL

Control how long reads are cached at edge locations.

const data = await env.MY_KV.get("static-config", { type: "json", cacheTtl: 3600, // Cache for 1 hour });

Minimum: 60 seconds. Default: 60 seconds.

Use cases:

  • Increase for write-rarely data (static configs)

  • Keep low or default for frequently-updated data

Bulk Operations

Bulk operations via Wrangler or REST API only (not Workers binding).

Wrangler bulk write

Create JSON file

cat > data.json << 'EOF' [ { "key": "user:1", "value": "{"name":"Alice"}" }, { "key": "user:2", "value": "{"name":"Bob"}", "expiration_ttl": 3600 } ] EOF

npx wrangler kv bulk put data.json --binding MY_KV

Wrangler bulk delete

cat > keys.json << 'EOF' ["user:1", "user:2", "user:3"] EOF

npx wrangler kv bulk delete keys.json --binding MY_KV

Limits: 10,000 keys per request, 100 MB total.

See bulk.md for REST API examples.

Wrangler Commands

Namespace

wrangler kv namespace create <NAME> wrangler kv namespace list wrangler kv namespace delete --namespace-id <ID>

Key operations

wrangler kv key put <KEY> <VALUE> --binding <BINDING> wrangler kv key get <KEY> --binding <BINDING> wrangler kv key delete <KEY> --binding <BINDING> wrangler kv key list --binding <BINDING> [--prefix <PREFIX>]

With options

wrangler kv key put <KEY> <VALUE> --binding <BINDING> --ttl 3600 wrangler kv key put <KEY> --binding <BINDING> --path ./file.txt

Bulk

wrangler kv bulk put <FILE.json> --binding <BINDING> wrangler kv bulk delete <FILE.json> --binding <BINDING>

Consistency Model

KV is eventually consistent:

  • Writes propagate globally in ~60 seconds (or cacheTtl )

  • Writes are immediately visible at the origin location

  • Concurrent writes to same key: last write wins

  • Negative lookups (key not found) are cached

Write rate limit

1 write per second per key. Exceeding causes 429 errors.

// Implement retry with backoff async function putWithRetry(key: string, value: string, retries = 3) { for (let i = 0; i < retries; i++) { try { await env.MY_KV.put(key, value); return; } catch (e) { if (i < retries - 1) await sleep(1000 * Math.pow(2, i)); else throw e; } } }

When to use Durable Objects instead

  • Need strong consistency

  • Need atomic operations

  • Need > 1 write/sec to same key

  • Need transactions

Limits

Parameter Free Paid

Reads/day 100,000 Unlimited

Writes/day 1,000 Unlimited

Storage 1 GB Unlimited

Namespaces 1,000 1,000

Ops/Worker invocation 1,000 1,000

Parameter Limit

Key size 512 bytes

Value size 25 MiB

Metadata size 1,024 bytes

Keys per list() 1,000

Keys per multi-get 100

Write rate (same key) 1/sec

Minimum TTL 60 sec

Pricing

Free plan (per day):

  • 100,000 reads

  • 1,000 writes/deletes/lists

  • 1 GB storage

Paid plan (per month):

Metric Included Overage

Reads 10 million $0.50/million

Writes 1 million $5.00/million

Deletes 1 million $5.00/million

Lists 1 million $5.00/million

Storage 1 GB $0.50/GB

No egress fees. Dashboard/Wrangler queries are billable.

See pricing.md for optimization tips.

Best Practices

Use prefixes for organization

// Good: prefixed keys await env.MY_KV.put("user:123:profile", data); await env.MY_KV.put("user:123:settings", data); await env.MY_KV.list({ prefix: "user:123:" });

// Bad: flat keys await env.MY_KV.put("user_123_profile", data);

Store small values in metadata

// Avoid list() + get() for each key await env.MY_KV.put(key, "", { metadata: { value: smallValue } });

Coalesce related data

// Instead of many small keys await env.MY_KV.put( "user:123:settings", JSON.stringify({ theme: "dark", language: "en", notifications: true, }) );

Handle missing keys

const value = await env.MY_KV.get("key", "json"); if (value === null) { // Key doesn't exist or expired }

Local Development

Uses local KV by default

wrangler dev

Use remote/production KV

wrangler dev --remote

Or set "remote": true in binding config.

Prohibitions

  • ❌ Do not write to same key more than 1/sec

  • ❌ Do not rely on immediate consistency after writes

  • ❌ Do not use KV for atomic counters (use Durable Objects)

  • ❌ Do not exceed 25 MiB value size

  • ❌ Do not use bulk write via Workers binding (use REST API)

References

  • binding.md — Binding configuration

  • api.md — Workers API reference

  • bulk.md — Bulk operations

  • pricing.md — Billing and optimization

Links

  • Documentation

  • Changelog

Related Skills

  • cloudflare-workers — Worker development

  • cloudflare-pages

  • Pages Functions with KV

  • cloudflare-durable-objects

  • Strong consistency alternative

  • cloudflare-d1 — D1 SQL database operations

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

react-testing-library

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

social-writer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

commits

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

mantine-dev

No summary provided by upstream source.

Repository SourceNeeds Review