kv
1
总安装量
0
周安装量
#53451
全站排名
安装命令
npx skills add https://github.com/null-shot/cloudflare-skills --skill kv
Skill 文档
Workers KV
Fast, eventually-consistent key-value storage distributed globally across Cloudflare’s edge network.
FIRST: Create KV Namespace
# 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
// 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
{
"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:
wrangler types
Session Authentication Pattern
Complete session management using KV for token storage with Hono router:
// 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:
// 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)
// 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
// 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
// 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
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
async function checkRateLimit(env: Env, ip: string): Promise<boolean> {
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)
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
// 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
// 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
// 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 – Advanced patterns: cache invalidation, distributed locks, A/B testing
- references/limits.md – Size limits, rate limits, consistency model
- references/testing.md – Vitest integration, mocking KV, test isolation
Best Practices
- Use TTL for temporary data: Always set expiration for sessions, caches, rate limits
- Prefix your keys: Makes listing and management easier (
user:,session:,cache:) - Don’t rely on immediate consistency: KV is eventually consistent (usually <60s)
- Handle null values: Keys not found or expired return
null - Use metadata for filtering: Store timestamps, versions, tags without reading values
- Batch operations: Use
list()with prefix instead of individual gets - Cache at the edge: KV is fastest when accessed from same region as user
- Monitor costs: Writes are more expensive than reads, optimize write patterns
- Use Durable Objects for coordination: Don’t use KV for locks or transactions
- Generate types: Run
wrangler typesafter config changes for type safety