Workers KV — Fast Reads, Patient Writes
Hoplink is the perfect KV workload: a link like cat is created once, then read thousands of times from all over the world. Workers KV is a global key-value store built for exactly that pattern. Using it well means knowing both its four operations and the one promise it does not make.
The rule
Read often, write rarely, and accept that writes spread slowly.
The binding env.LINKS gives you four core operations:
// write, with optional expiry and metadata
await env.LINKS.put("cat", "https://example.com/all-about-cats", {
expirationTtl: 60 * 60 * 24 * 30, // 30 days, in seconds
metadata: { owner: "priya", created: "2026-09-01" },
});
const target = await env.LINKS.get("cat"); // string or null
const { value, metadata } = await env.LINKS.getWithMetadata("cat");
await env.LINKS.delete("cat");
// a second index key per link, like "priya:cat", lets you list one owner's links
const page = await env.LINKS.list({ prefix: "priya:" });
// page.keys, page.list_complete, page.cursorWhat each part means:
- Keys and values. Keys are strings; values can be text, JSON (
get(key, "json")), or binary data. - Metadata is a small JSON object stored beside the value.
list()returns it with each key, so you can show "created by priya" without reading every value. - Expiry.
expirationTtl(seconds from now, at least 60) orexpiration(a timestamp) makes KV delete the key for you. - list() returns keys in pages. Keep calling with
cursoruntillist_completeis true. - Bulk operations. Wrangler can load thousands of pairs at once with
wrangler kv bulk put links.json --binding LINKS, handy for importing links from an old system.
The missing promise is strong consistency. KV caches values in the locations that read them. After a put, a reader in the same location usually sees the change quickly, but other locations can keep serving the old value for up to about a minute. Each key also accepts only about one write per second.
The shortcut
Before storing anything in KV, ask "Would a stale read for a minute hurt anyone?" If not, KV is a great fit. If it would — a bank balance, a live counter, "is this username taken?" — use D1 or a Durable Object instead.
Short code to long URL
Written once, read constantly, and a new link appearing a few seconds late in Chile harms nobody. KV.
Trap: using get then put to "reserve" a short code. Two people in different cities can both see null and both write — the last write wins, and one person's link is silently replaced.
Drills
Answer fast — trust the rule.
What does await env.LINKS.get("dog") return when the key does not exist?
Hoplink's list page shows each link's owner without reading every value. Which feature makes that possible?
A link is updated in London. A visitor in Tokyo opens it ten seconds later. What might they see?
Which Hoplink feature should NOT be built on KV?
A list({ prefix: "priya:" }) call returns list_complete: false. What should you do next?
Remember
- Read often, write rarely, and accept that writes spread slowly.
- Shortcut: would a minute of stale data hurt anyone? If yes, not KV.
- Trap: get-then-put to reserve unique keys — last write wins.
