State & Cache

Key-value storage, durable or fast

Two key-value stores for two jobs. State is durable, backed by SQLite, and exposes a KV-compatible API plus atomic JSON updates, so counters and documents never need a read-modify-write cycle. Cache is a fast, best-effort store with optional TTLs for rate limits, sessions, and computed results.

State & Cache in Action

Keys

Durable state has no expiry; cache entries carry a TTL

KeyValueStoreTTL
stats{ "visits": 10482, "signups": 231 }
STATE
—
user:123{ "role": "admin", "visits": 42 }
STATE
—
feature:dark-mode"true"
STATE
—
report:q3<cached 14 KB>
CACHE
9m 12s
ratelimit:203.0.113.4"1"
CACHE
0.4s

Atomic Update

Increment a counter without reading it first

src/index.ts
await env.STATE.update("user:123", {
  $inc: { visits: 1 },
  $set: { lastSeen: Date.now() },
});

Key Features

KV-Compatible State

get, put, delete, getWithMetadata, and list with the same shapes as the KV API you already know.

Atomic JSON Updates

$set, $inc, $push, $pop, and $unset apply directly in storage, so concurrent writers never clobber each other.

Cache With TTL

Set a value for sixty seconds or an hour and let it expire. Treat it as best-effort and fall back when it is gone.

Multiple Bindings

Attach several state or cache stores to one worker and keep user data separate from config.

State and Cache Bindings

Durable state and an ephemeral cache, side by side in the same worker.

src/index.ts
export default {
  async fetch(request, env) {
    await env.STATE.update("stats", { $inc: { visits: 1 } });

    const cached = await env.CACHE.get("report");
    if (cached) {
      return new Response(cached);
    }

    const report = await buildReport(env);
    await env.CACHE.set("report", report, { ttl: 600 });
    return new Response(report);
  },
} satisfies Ploy;

Ready to get started?

Deploy your first project in minutes with Ploy.