From zero to a live command.
GACreate a namespace, mint a token, install an SDK, and touch all three primitives. About five minutes, no infrastructure to stand up.
Create a namespace + token
A namespace is where you choose a backend and consistency; a token is what a client authenticates with. Create both in the dashboard at /<org>/cache, or from the CLI:
# In the dashboard at /<org>/cache, or from the CLI:
tilldev cache namespaces create app --backend upstash --consistency strong
tilldev cache tokens create --namespace app --scope read-write # → tc_live_…Copy the tc_… token it prints — you only see it once. Pick read-write for a server, read-only for anything you can’t fully trust. More on backends in Backends & vendors.
Install an SDK
pnpm add @tilldev/cache-nodeOn the edge use @tilldev/cache-edge; on Cloudflare Workers use @tilldev/cache-cloudflare. Same API on all three.
Connect a client
// cache.ts
import { createClient } from '@tilldev/cache-node'
// createClient() reads TILLCACHE_URL from the environment:
// TILLCACHE_URL=tillcache://tc_live_xxx@cache.tilldev.dev
export const cache = createClient()tc_ token. Set TILLCACHE_URL as an environment variable — never commit it, never ship a read-write token to a browser bundle.Your first KV command
import { cache } from './cache'
await cache.set('greeting', 'hello', { ex: 60 }) // 60s TTL
const g = await cache.get('greeting') // 'hello'
await cache.incr('visits') // atomic counter
const visits = await cache.get('visits') // '1'The KV surface is ioredis-compatible — full method list on the KV page.
Push and consume a job
import { cache } from './cache'
const q = cache.queue('emails')
// Producer:
await q.push({ to: 'ada@example.com', template: 'welcome' }, { dedupeKey: 'welcome:ada' })
// Consumer:
await q.consume(async (msg) => {
await sendEmail(msg.body) // throw to retry; return to ack
}, { visibilitySeconds: 30 })At-least-once delivery, a dedupeKey for idempotency, and a dead-letter queue after repeated failures. Details on the Queues page.
Publish and subscribe
import { cache } from './cache'
// Subscriber (SSE under the hood):
const ac = new AbortController()
cache.subscribe('deploys', (message) => {
console.log('deploy event', message)
}, { signal: ac.signal })
// Publisher, anywhere:
await cache.publish('deploys', { service: 'web', status: 'live' })Delivery is over Server-Sent Events. More on the Pub/Sub page.
What's next
- KV — the full ioredis-compatible surface, pipelines, and the raw
command()hatch. - Queues — visibility timeouts, retries, and the dead-letter queue.
- Pub/Sub — SSE fan-out with clean teardown.
- Backends & vendors — Upstash, Durable Objects, or bring-your-own Redis.