KV Storage
Cloudflare Workers KV is a global key-value store optimized for read-heavy workloads. Quickback uses KV for session storage, caching, and fast lookups.
What is KV?
Workers KV is a distributed key-value store:
- Global distribution - Data replicated to 300+ edge locations
- Low-latency reads - Cached at the edge, sub-millisecond access
- Eventually consistent - Writes propagate globally within 60 seconds
- Simple API - Get, put, delete, list
Use Cases in Quickback
| Use Case | Description |
|---|---|
| Sessions | Better Auth stores sessions in KV for fast validation |
| Cache | Cache expensive database queries or API responses |
| Rate Limiting | Track request counts per user/IP |
| Feature Flags | Store configuration that changes infrequently |
Namespace Setup
Create a KV namespace via Wrangler:
# Create namespace
wrangler kv namespace create "SESSIONS"
# Output:
# Add the following to your wrangler.toml:
# [[kv_namespaces]]
# binding = "SESSIONS"
# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Add to wrangler.toml:
[[kv_namespaces]]
binding = "SESSIONS"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[[kv_namespaces]]
binding = "CACHE"
id = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"Reading and Writing Values
Basic Operations
// Write a value
await env.CACHE.put("user:123", JSON.stringify({ name: "Alice" }));
// Read a value
const data = await env.CACHE.get("user:123");
const user = data ? JSON.parse(data) : null;
// Delete a value
await env.CACHE.delete("user:123");
// Check if key exists
const exists = await env.CACHE.get("user:123") !== null;With Metadata
KV supports metadata attached to keys:
// Write with metadata
await env.CACHE.put("user:123", JSON.stringify(userData), {
metadata: { updatedAt: Date.now(), version: 1 },
});
// Read with metadata
const { value, metadata } = await env.CACHE.getWithMetadata("user:123");List Keys
// List all keys with prefix
const list = await env.CACHE.list({ prefix: "user:" });
for (const key of list.keys) {
console.log(key.name, key.metadata);
}
// Paginate through results
let cursor = undefined;
do {
const result = await env.CACHE.list({ prefix: "user:", cursor });
// Process result.keys
cursor = result.cursor;
} while (cursor);Expiration and TTL
Set automatic expiration on keys:
// Expire in 1 hour (3600 seconds)
await env.CACHE.put("session:abc", sessionData, {
expirationTtl: 3600,
});
// Expire at specific timestamp
await env.CACHE.put("session:abc", sessionData, {
expiration: Math.floor(Date.now() / 1000) + 3600,
});Common TTL patterns:
| Use Case | TTL |
|---|---|
| Session tokens | 24 hours (86400) |
| API cache | 5 minutes (300) |
| Rate limit counters | 1 minute (60) |
| Feature flags | No expiration |
Session Storage
Better Auth uses KV for session storage in Cloudflare deployments:
// Quickback configures this automatically
auth: defineAuth("better-auth", {
session: {
storage: "kv", // Uses SESSIONS namespace
},
}),Sessions are stored as:
- Key:
session:{sessionId} - Value: Serialized session object
- TTL: Configured session expiration
Caching Patterns
Cache-Aside Pattern
async function getUser(userId: string) {
// Check cache first
const cached = await env.CACHE.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
}
// Fetch from database
const user = await db.select().from(users).where(eq(users.id, userId));
// Store in cache
await env.CACHE.put(`user:${userId}`, JSON.stringify(user), {
expirationTtl: 300, // 5 minutes
});
return user;
}Cache Invalidation
// Invalidate on update
async function updateUser(userId: string, data: UserUpdate) {
await db.update(users).set(data).where(eq(users.id, userId));
await env.CACHE.delete(`user:${userId}`);
}wrangler.toml Reference
# Production namespace
[[kv_namespaces]]
binding = "SESSIONS"
id = "abc123..."
# Preview namespace (for wrangler dev)
[[kv_namespaces]]
binding = "SESSIONS"
id = "def456..."
preview_id = "ghi789..."Limitations
- Value size - Maximum 25 MB per value
- Key size - Maximum 512 bytes
- Write limits - 1 write per second per key
- Eventual consistency - Changes may take up to 60 seconds to propagate
- Not for hot writes - Use Durable Objects for high-write scenarios
KV is optimized for read-heavy workloads. For write-heavy use cases or strong consistency, consider Durable Objects.