Storage bindings

Choose the right storage product

Match relational queries, read-heavy keys, objects, and strongly coordinated state to D1, KV, R2, or Durable Objects.

Link Vault now has three storage bindings, and a fourth is coming. All of them “save data”, so the question I get most is: which one for what?

The short version I keep in my head:

  • D1 for records you query by more than one field. Relational data, SQL, transactions.
  • KV for values you read a lot and can tolerate a bit stale. Caches, config, flags.
  • R2 for files. Anything large or binary, addressed by a key.
  • Durable Objects for state that needs one place to agree. Counters, locks, rooms.

The products differ in consistency, query model, size limits, and coordination. Pricing and limits change over time, so check the current docs the day you commit to a design.

Start from the invariant

Don’t start from the product. Start from the rule your data must obey, then pick what can enforce it.

“A user can never claim the same name twice” needs a transaction or a single coordinator. That’s D1 with a UNIQUE constraint, or a Durable Object. KV cannot promise it, because two writes in two regions don’t see each other in time.

“The public list may be one minute old” is a cache. KV, with a TTL.

“Download this exact export” is an object with a key. R2.

“Find archived links from last month, newest first” is a query by several fields. D1.

Two things that look like storage but aren’t

Queues carry messages from a producer to a consumer. They are delivery, not the source of truth. Lose a message or get it twice, and your data must stay correct.

Secrets belong in the secret store, read through env. Never in D1, KV, or R2, even encrypted, even “just for now”.

Six questions per datum

For each piece of data, write down the authoritative copy, the acceptable staleness, the access pattern, the size, the retention, and the recovery method.

When two products hold copies, say which one wins. In Link Vault, D1 wins over the KV cache, which is rebuilt from D1 on a miss. That one sentence prevents a whole class of bugs.

The mistake I see most

People reach for KV first because it looks simplest, then discover they need to filter and count. KV lists keys by prefix, but it is not a query engine. Moving that data to D1 later means a migration under load. Choose relational from day one when you need it.

Now classify Link Vault’s data: link records, the cached public list, export files, a live collaboration room, API secrets, and export job messages. Assign each to D1, KV, R2, Durable Objects, the secret store, or Queues, with a one-line reason.

Lesson completed