Cloudflare KV Reference Sheet

Just a guy who loves to write code and watch anime.
Search for a command to run...

Just a guy who loves to write code and watch anime.
No comments yet. Be the first to comment.
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

KV Namespace = The actual database/storage (has ID like abc123def456)
Binding = Variable name in your code (like TODO, USERS)
Key-Value Store = Simple hash map, not a relational database
Eventually Consistent = Changes take time to propagate globally (up to 60 seconds)
Optimized for reads = Great for caching, not for frequent writes
{
"name": "my-worker",
"kv_namespaces": [
{
"binding": "TODO", // Variable name in code
"id": "06779da6940b431db6e566b4846d64db", // Production namespace
"preview_id": "06779da6940b431db6e566b484a6a7" // Dev namespace
}
]
}
wrangler dev # Local fake KV (starts empty)
wrangler dev --remote # Uses preview_id (real KV, dev data)
wrangler deploy # Uses id (production data)
// Write
await env.TODO.put("task:123", "Buy groceries");
// Read
let value = await env.TODO.get("task:123"); // Returns string or null
// Delete
await env.TODO.delete("task:123"); // No error if key doesn't exist
// Store JSON (manual)
const task = { id: 123, title: "Buy groceries", done: false };
await env.TODO.put("task:123", JSON.stringify(task));
// Read JSON (manual)
let taskJson = await env.TODO.get("task:123");
const task = taskJson ? JSON.parse(taskJson) : null;
// Read JSON (automatic parsing)
const task = await env.TODO.get("task:123", "json"); // Returns object or null
// Update JSON (MUST put back!)
const task = await env.TODO.get("task:123", "json");
if (task) {
task.done = true;
await env.TODO.put("task:123", JSON.stringify(task)); // Required!
}
// Expire in X seconds
await env.TODO.put("session:abc", sessionData, {
expirationTtl: 3600, // 1 hour
});
// Expire at specific time
const midnight = new Date();
midnight.setHours(24, 0, 0, 0);
await env.TODO.put("daily-stats", statsData, {
expiration: Math.floor(midnight.getTime() / 1000), // Unix timestamp
});
60 = 1 minute
300 = 5 minutes
3600 = 1 hour
86400 = 24 hours
604800 = 1 week
await env.TODO.put("user:123", userData, {
metadata: {
version: 1,
lastUpdated: Date.now(),
source: "api",
},
expirationTtl: 3600,
});
const result = await env.TODO.getWithMetadata("user:123", "json");
if (result.value !== null) {
const user = result.value; // Your data
const meta = result.metadata; // Your metadata object
}
// Hierarchical with colons
"user:123:profile";
"user:123:settings";
"user:123:sessions:abc";
// Cache keys
"cache:weather:london:2025-08-09";
"api:github:users:123";
// Feature flags
"feature:new-ui:production";
"feature:beta-test:enabled";
// Rate limiting
"ratelimit:192.168.1.1:2025-08-09-14"; // IP + hour
"user 123 profile"; // Spaces
"user@123#profile"; // Special characters
"john_profile"; // No structure
"userprofilejohn"; // Hard to read
const result = await env.TODO.list();
// Returns: { keys: [...], truncated: boolean, cursor: string }
const userKeys = await env.TODO.list({ prefix: "user:123:" });
// Only returns keys starting with "user:123:"
let allKeys = [];
let cursor = undefined;
do {
const result = await env.TODO.list({
prefix: "user:",
limit: 100,
cursor: cursor,
});
allKeys.push(...result.keys);
cursor = result.cursor;
} while (result.truncated);
⚠️ Warning: list() only returns key names and metadata, NOT values. Still need get() for actual data.
try {
const data = await env.TODO.get("user:123", "json");
if (data === null) {
return new Response("Not found", { status: 404 });
}
// Use data...
} catch (error) {
if (error.message.includes("JSON")) {
// Corrupted JSON data
await env.TODO.delete("user:123"); // Clean up bad data
return new Response("Data corrupted", { status: 422 });
}
// Network or other error
return new Response("Service unavailable", { status: 503 });
}
// Always provide defaults
const preferences = (await env.TODO.get("user:123:prefs", "json")) || {
theme: "light",
notifications: true,
};
// Safe property access
const theme = (preferences && preferences.theme) || "light";
const cacheKey = `api:weather:${city}`;
let weather = await env.TODO.get(cacheKey, "json");
if (!weather) {
const response = await fetch(`https://api.weather.com/${city}`);
weather = await response.json();
// Cache for 5 minutes
await env.TODO.put(cacheKey, JSON.stringify(weather), {
expirationTtl: 300,
});
}
return new Response(JSON.stringify(weather));
// Create session
await env.TODO.put(
`session:${sessionId}`,
JSON.stringify({
userId: 123,
loginTime: Date.now(),
}),
{ expirationTtl: 3600 }
); // 1 hour
// Check session
const session = await env.TODO.get(`session:${sessionId}`, "json");
if (!session) {
return new Response("Session expired", { status: 401 });
}
// Set flag
await env.TODO.put("feature:new-ui:enabled", "true");
// Check flag
const enabled = (await env.TODO.get("feature:new-ui:enabled")) === "true";
if (enabled) {
// Show new UI
}
const minute = Math.floor(Date.now() / 60000);
const rateLimitKey = `ratelimit:${ip}:${minute}`;
let requests = await env.TODO.get(rateLimitKey);
requests = requests ? parseInt(requests) + 1 : 1;
if (requests > 100) {
return new Response("Rate limited", { status: 429 });
}
await env.TODO.put(rateLimitKey, requests.toString(), {
expirationTtl: 60, // Expire after 1 minute
});
Key name: 512 bytes max
Value size: 25MB max per value
Metadata: No specific limit (but keep reasonable)
Reads: Very fast (especially after first access)
Writes: Eventually consistent (up to 60 seconds globally)
List operations: Slow and expensive, avoid in hot paths
Changes visible immediately at write location
May take 60+ seconds to appear in other regions
Negative lookups (key doesn't exist) are also cached
If you create a key after checking it doesn't exist, other regions might still return null
❌ Avoid KV for:
Frequent updates to same key (>10/second)
Transactions or atomic operations
Complex queries or relationships
Real-time consistency requirements
Large datasets you need to iterate through
✅ Perfect for:
Caching API responses
User sessions and preferences
Configuration and feature flags
Static asset serving
Rate limiting counters
Read-heavy workloads