Workers KV
Learning Focus
By the end of this lesson you will understand how Workers KV stores and reads data globally, its consistency model, and when to use it vs other storage options.
What Is Workers KV?
Workers KV is a global, low-latency, key-value data store. It's designed for read-heavy workloads where data is written infrequently but read millions of times across 330+ edge locations.
flowchart LR
WORKER["Worker\n(Any PoP)"] -->|Read| KV["KV Store\n(Global Edge Cache)"]
WORKER -->|Write| KV
KV -->|"Propagates in\n~60 seconds"| GLOBAL["All 330+\nEdge Locations"]
style KV fill:#16a34a,color:#fff,stroke:#15803d
style WORKER fill:#f6821f,color:#fff,stroke:#e5711e
Key Characteristics
| Feature | Details |
|---|---|
| Read latency | <10ms (reading from nearest edge) |
| Write propagation | Eventually consistent (~60 seconds globally) |
| Max key size | 512 bytes |
| Max value size | 25 MB |
| Consistency | Eventually consistent (not strongly consistent) |
| Best for | Configuration, feature flags, cached API responses, user sessions |
Free Tier
| Resource | Free Plan |
|---|---|
| Reads | 100,000 per day |
| Writes | 1,000 per day |
| Deletes | 1,000 per day |
| Lists | 1,000 per day |
| Storage | 1 GB |
| Namespaces | 100 |
Using KV in a Worker
Setup
Create a KV namespace
wrangler kv namespace create MY_KV
# Output:
# Add the following to your wrangler.toml:
# [[kv_namespaces]]
# binding = "MY_KV"
# id = "<namespace-id>"
wrangler.toml
name = "my-worker"
main = "src/index.ts"
[[kv_namespaces]]
binding = "MY_KV"
id = "<namespace-id>"
Basic Operations
src/index.ts
export interface Env {
MY_KV: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Write a value
if (url.pathname === "/set") {
await env.MY_KV.put("greeting", "Hello from KV!");
return new Response("Value saved");
}
// Read a value
if (url.pathname === "/get") {
const value = await env.MY_KV.get("greeting");
return new Response(value || "No value found");
}
// Store JSON
if (url.pathname === "/set-json") {
await env.MY_KV.put("user:123", JSON.stringify({
name: "Alice",
role: "admin",
}));
return new Response("JSON saved");
}
// Read JSON
if (url.pathname === "/get-json") {
const user = await env.MY_KV.get("user:123", "json");
return Response.json(user);
}
// Delete a value
if (url.pathname === "/delete") {
await env.MY_KV.delete("greeting");
return new Response("Value deleted");
}
// List keys
if (url.pathname === "/list") {
const keys = await env.MY_KV.list();
return Response.json(keys);
}
return new Response("Not Found", { status: 404 });
},
};
TTL (Expiration)
// Set a value that expires in 1 hour
await env.MY_KV.put("session:abc", "user-data", {
expirationTtl: 3600, // seconds
});
// Set a value that expires at a specific time
await env.MY_KV.put("promo:summer", "active", {
expiration: Math.floor(Date.now() / 1000) + 86400, // Unix timestamp
});
When to Use KV (vs Other Storage)
| Storage | Best For | Consistency | Free? |
|---|---|---|---|
| Workers KV | Read-heavy, globally distributed data | Eventually consistent | ✅ |
| D1 | Relational data, complex queries | Strong (per-region) | ✅ (limited) |
| Durable Objects | Real-time collaboration, counters, WebSockets | Strongly consistent | ✅ (limited) |
| R2 | Large files, S3-compatible storage | Strong | 💰 Paid |
When NOT to Use KV
KV is not suitable for data that needs immediate consistency. If you write a value and read it immediately from a different location, you might get the old value. For strong consistency, use Durable Objects or D1.
Key Takeaways
- Workers KV is a global key-value store optimized for read-heavy workloads.
- Reads are fast (
<10ms), writes propagate globally in ~60 seconds. - Free tier: 100k reads/day, 1k writes/day, 1 GB storage.
- Support for JSON values, TTL expiration, and key listing.
- Use KV for configuration, sessions, and cached data — not for data requiring strong consistency.
What's Next
- Continue to D1 SQL Database to learn about serverless SQL at the edge.