🎯

kv

🎯Skill

from null-shot/cloudflare-skills

VibeIndex|
What it does

Stores and retrieves globally distributed key-value data for fast edge caching, session management, configuration, and user preferences.

πŸ“¦

Part of

null-shot/cloudflare-skills(11 items)

kv

Installation

npxRun with npx
npx skills-ref validate ./agents-sdk
npxRun with npx
npx skills-ref validate ./*/
πŸ“– Extracted from docs: null-shot/cloudflare-skills
1Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Eventually-consistent key-value storage distributed globally. Load when implementing session storage, authentication tokens, caching API responses, feature flags, A/B testing, rate limiting, or storing user preferences at the edge.

Overview

# Workers KV

Fast, eventually-consistent key-value storage distributed globally across Cloudflare's edge network.

FIRST: Create KV Namespace

```bash

# Create namespace

wrangler kv namespace create MY_KV

# Create preview namespace for dev

wrangler kv namespace create MY_KV --preview

# Add to wrangler.jsonc (use IDs from commands above)

```

When to Use

| Use Case | Why KV |

|----------|---------|

| Session storage | Fast token/session lookups at the edge |

| Caching | Cache API responses, rendered HTML, or computed results |

| Configuration data | Feature flags, app settings, rate limit counters |

| User profiles | Store user preferences, settings, metadata |

| A/B testing | Store experiment assignments and feature toggles |

| Rate limiting | Track request counts per user/IP with TTL |

Don't use KV for:

  • Strongly consistent data (use Durable Objects)
  • Relational queries (use D1)
  • Large files >25MB (use R2)
  • Transactional operations

Quick Reference

| Operation | API | Example |

|-----------|-----|---------|

| Read value | get(key) | await env.KV.get("user:123") |

| Read with metadata | getWithMetadata(key) | await env.KV.getWithMetadata("key", "json") |

| Write value | put(key, value, options) | await env.KV.put("key", "value", { expirationTtl: 3600 }) |

| Delete value | delete(key) | await env.KV.delete("key") |

| List keys | list(options) | await env.KV.list({ prefix: "user:" }) |

Value Types

```typescript

// String (default)

await env.KV.get("key")

// JSON (auto-parse)

await env.KV.get("user:123", "json")

// Binary

await env.KV.get("image", "arrayBuffer")

// Stream

await env.KV.get("large-file", "stream")

```

wrangler.jsonc Configuration

```jsonc

{

"name": "my-worker",

"main": "src/index.ts",

"compatibility_date": "2026-01-01",

"kv_namespaces": [

{

"binding": "AUTH_TOKENS",

"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",

"preview_id": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"

},

{

"binding": "CACHE",

"id": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"

}

]

}

```

After adding bindings, generate types:

```bash

wrangler types

```

Session Authentication Pattern

Complete session management using KV for token storage with Hono router:

```typescript

// src/index.ts

import { Hono } from 'hono'

import { cors } from 'hono/cors'

interface Env {

AUTH_TOKENS: KVNamespace;

}

const app = new Hono<{ Bindings: Env }>()

// Add CORS middleware

app.use('*', cors())

app.get('/', async (c) => {

try {

// Get token from header or cookie

const token = c.req.header('Authorization')?.slice(7) ||

c.req.header('Cookie')?.match(/auth_token=([^;]+)/)?.[1];

if (!token) {

return c.json({

authenticated: false,

message: 'No authentication token provided'

}, 403)

}

// Check token in KV

const userData = await c.env.AUTH_TOKENS.get(token)

if (!userData) {

return c.json({

authenticated: false,

message: 'Invalid or expired token'

}, 403)

}

return c.json({

authenticated: true,

message: 'Authentication successful',

data: JSON.parse(userData)

})

} catch (error) {

console.error('Authentication error:', error)

return c.json({

authenticated: false,

message: 'Internal server error'

}, 500)

}

})

export default app

```

Store a session token:

```typescript

// Create token (e.g., on login)

const token = crypto.randomUUID()

const userData = { userId: 123, email: "user@example.com" }

// Store with 24-hour expiration

await env.AUTH_TOKENS.put(

token,

JSON.stringify(userData),

{ expirationTtl: 86400 }

)

```

Key Design Patterns

TTL (Time-to-Live)

```typescript

// Expire after 1 hour

await env.KV.put("cache:api-response", data, {

expirationTtl: 3600

})

// Expire at specific time

await env.KV.put("promo:summer", data, {

expiration: Math.floor(Date.now() / 1000) + 86400

})

```

Key Naming Conventions

```typescript

// Use prefixes for organization and listing

"user:123" // User data

"session:abc123" // Session tokens

"cache:api:/users" // API cache

"config:feature:new" // Feature flags

"rate:ip:1.2.3.4" // Rate limiting

// List all user keys

const { keys } = await env.KV.list({ prefix: "user:" })

```

Metadata

```typescript

// Store metadata with value

await env.KV.put("user:123", userData, {

metadata: {

created: Date.now(),

version: "v1",

tags: ["premium", "verified"]

}

})

// Read with metadata

const { value, metadata } = await env.KV.getWithMetadata("user:123", "json")

console.log(metadata.tags) // ["premium", "verified"]

```

Caching Pattern

```typescript

async function getCachedData(env: Env, key: string) {

// Try cache first

const cached = await env.CACHE.get(key, "json")

if (cached) return cached

// Fetch from origin

const data = await fetchFromAPI()

// Cache for 5 minutes

await env.CACHE.put(key, JSON.stringify(data), {

expirationTtl: 300

})

return data

}

```

Rate Limiting

```typescript

async function checkRateLimit(env: Env, ip: string): Promise {

const key = rate:${ip}

const count = await env.KV.get(key)

if (!count) {

// First request

await env.KV.put(key, "1", { expirationTtl: 60 })

return true

}

const current = parseInt(count)

if (current >= 100) {

return false // Rate limit exceeded

}

// Increment counter

await env.KV.put(key, String(current + 1), { expirationTtl: 60 })

return true

}

```

Edge Cases

Handling 404s (Key Not Found)

```typescript

const value = await env.KV.get("key")

if (value === null) {

// Key doesn't exist or has expired

console.log("Key not found")

}

// For JSON, check before parsing

const userData = await env.KV.get("user:123")

if (userData) {

const user = JSON.parse(userData)

}

// Or use "json" type (returns null if not found)

const user = await env.KV.get("user:123", "json")

```

Eventual Consistency

```typescript

// Write may take seconds to propagate globally

await env.KV.put("counter", "1")

// Immediate read might return old value or null

const value = await env.KV.get("counter") // Could be null or "1"

// For strong consistency, use Durable Objects instead

```

Size Limits

```typescript

// Keys: max 512 bytes

// Values: max 25 MB

// Metadata: max 1024 bytes

// For larger data, use R2:

if (data.length > 25 1024 1024) {

await env.BUCKET.put("large-file", data)

} else {

await env.KV.put("small-file", data)

}

```

List Pagination

```typescript

// List returns max 1000 keys per call

let cursor: string | undefined

const allKeys: string[] = []

do {

const result = await env.KV.list({

prefix: "user:",

cursor

})

allKeys.push(...result.keys.map(k => k.name))

cursor = result.list_complete ? undefined : result.cursor

} while (cursor)

```

Detailed References

  • [references/patterns.md](references/patterns.md) - Advanced patterns: cache invalidation, distributed locks, A/B testing
  • [references/limits.md](references/limits.md) - Size limits, rate limits, consistency model
  • [references/testing.md](references/testing.md) - Vitest integration, mocking KV, test isolation

Best Practices

  1. Use TTL for temporary data: Always set expiration for sessions, caches, rate limits
  2. Prefix your keys: Makes listing and management easier (user:, session:, cache:)
  3. Don't rely on immediate consistency: KV is eventually consistent (usually <60s)
  4. Handle null values: Keys not found or expired return null
  5. Use metadata for filtering: Store timestamps, versions, tags without reading values
  6. Batch operations: Use list() with prefix instead of individual gets
  7. Cache at the edge: KV is fastest when accessed from same region as user
  8. Monitor costs: Writes are more expensive than reads, optimize write patterns
  9. Use Durable Objects for coordination: Don't use KV for locks or transactions
  10. Generate types: Run wrangler types after config changes for type safety

More from this repository10

🎯
queues🎯Skill

Enables reliable asynchronous message processing on Cloudflare Workers, supporting background tasks, batch operations, retry logic, and decoupled message handling.

🎯
building-ai-agent-on-cloudflare🎯Skill

Builds AI agents on Cloudflare with persistent state, real-time WebSockets, scheduled tasks, and tool integration.

🎯
analytics-engine🎯Skill

Enables high-performance, scalable event tracking and analytics by writing and querying event data with SQL across various use cases like user metrics, billing, and telemetry.

🎯
building-mcp-server-on-cloudflare🎯Skill

Builds and deploys secure, scalable Model Context Protocol servers on Cloudflare Workers with OAuth authentication and custom tools.

🎯
workers🎯Skill

Generates microservices and handles HTTP, scheduled, and queue events using TypeScript and Cloudflare Workers configuration.

🎯
wrangler🎯Skill

Manages Cloudflare Workers development and deployment with comprehensive CLI commands for resources like Workers, KV, R2, and more.

🎯
durable-objects🎯Skill

Skill

🎯
workflows🎯Skill

Skill

🎯
d1-database🎯Skill

Enables serverless SQLite database operations at the edge, supporting structured data, SQL queries, migrations, and complex relational data management in Cloudflare Workers.

🎯
agents-sdk🎯Skill

Enables building stateful, persistent AI agents with advanced features like RPC, scheduling, state management, and streaming chat on Cloudflare Workers.