Cookbook

Recipes for getting useful state before architecture gets heavy.

These examples show how to use TmpState behind real app boundaries: server routes, workflow collections, audit trails, webhook capture, QA repros, and expiry checks.

Read the API docs

Keep capabilities server-side

A database URL is a bearer secret. Browser code should call your app API, not TmpState directly.

Model around product actions

Use collections that match real workflows: leads, tasks, votes, runs, artifacts, events.

Export before you outgrow it

When a prototype becomes a product, GET $DB/__export and migrate intentionally.

Recipes

Copy the shape, not just the code. Each recipe includes the reason behind the pattern so you can decide when to keep it, extend it, or replace it.

Next.js route handler that hides the database URL

Your browser UI needs persistence, but the TmpState database URL must stay server-side.

This is the default production-shaped pattern. The client talks to your app API; only the server calls TmpState.

  • Validate and normalize user input before forwarding it.
  • Keep TMPSTATE_DB in .env.local, Vercel env vars, or another server-only secret store.
  • Return app-shaped responses if the UI should not know about TmpState document envelopes.
// app/api/leads/route.ts
const DB = process.env.TMPSTATE_DB!

export async function POST(request: Request) {
  const lead = await request.json()

  const response = await fetch(`${DB}/leads`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      name: String(lead.name ?? ""),
      email: String(lead.email ?? ""),
      source: "landing-page",
    }),
  })

  return Response.json(await response.json(), { status: response.status })
}

export async function GET() {
  const response = await fetch(`${DB}/leads?limit=25`, {
    cache: "no-store",
  })

  return Response.json(await response.json(), { status: response.status })
}

Hackathon task board

A team needs shared todos, demo blockers, ownership, and judging-day polish without spending time on auth.

A small task board is valuable during the event and usually disposable afterward.

  • Use one document per task so PATCH operations stay small.
  • Keep status values boring: todo, doing, blocked, done.
  • If the project continues after the event, extend from $1 or move to Pro.
# Create tasks
curl -X POST "$TMPSTATE_DB/tasks" \
  -H "Content-Type: application/json" \
  -d '{"title":"Connect checkout demo","owner":"Mina","status":"doing"}'

curl -X POST "$TMPSTATE_DB/tasks" \
  -H "Content-Type: application/json" \
  -d '{"title":"Write judge script","owner":"Ravi","status":"todo"}'

# List tasks
curl "$TMPSTATE_DB/tasks?limit=50"

# Update a task
curl -X PATCH "$TMPSTATE_DB/tasks/doc_abc123" \
  -H "Content-Type: application/json" \
  -d '{"status":"done"}'

Agent scratchpad with durable checkpoints

A coding agent needs to remember choices, generated artifacts, checks, and follow-up tasks across tool calls.

Separate append-only collections make the run inspectable and reduce the risk of overwriting useful history.

  • Prefer append-only records for decisions and checks.
  • Keep large generated files in the repo or object storage; store paths and summaries in TmpState.
  • Use the admin URL to audit the run before handoff.
# Decision log
curl -X POST "$TMPSTATE_DB/decisions" \
  -H "Content-Type: application/json" \
  -d '{"topic":"storage","decision":"use TmpState behind app API","reason":"prototype expires by default"}'

# Verification record
curl -X POST "$TMPSTATE_DB/checks" \
  -H "Content-Type: application/json" \
  -d '{"name":"lead form","command":"pnpm test","status":"passed"}'

# Artifact index
curl -X POST "$TMPSTATE_DB/artifacts" \
  -H "Content-Type: application/json" \
  -d '{"path":"app/api/leads/route.ts","kind":"source","summary":"server-only TmpState proxy"}'

Webhook inbox for prototypes

You need to inspect webhook payloads from Stripe, GitHub, Linear, or a form tool before deciding the real schema.

Raw payload capture is useful for discovery, but it should not become permanent storage by accident.

  • Do not capture secrets, payment card data, or regulated data.
  • After discovery, normalize the fields your app actually needs.
  • Use __export before expiry if the payload examples should become fixtures.
// app/api/webhook-inbox/route.ts
const DB = process.env.TMPSTATE_DB!

export async function POST(request: Request) {
  const payload = await request.json()

  await fetch(`${DB}/webhook_events`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      received_at: new Date().toISOString(),
      event_type: payload.type ?? payload.action ?? "unknown",
      payload,
    }),
  })

  return Response.json({ ok: true })
}

QA repro database

A bug report needs structured state: environment, steps, logs, expected result, actual result, and current triage status.

One database per repro gives engineering a private, inspectable bundle that expires if the bug stops mattering.

  • Keep logs short and redact tokens before writing.
  • Use separate collections for reports, steps, logs, and comments.
  • Delete the database immediately if a repro accidentally includes sensitive data.
curl -X POST "$TMPSTATE_DB/reports" \
  -H "Content-Type: application/json" \
  -d '{
    "title":"Checkout total changes after refresh",
    "browser":"Chrome 126",
    "severity":"high",
    "status":"new"
  }'

curl -X POST "$TMPSTATE_DB/steps" \
  -H "Content-Type: application/json" \
  -d '{"order":1,"text":"Add monthly Pro plan to cart"}'

curl -X POST "$TMPSTATE_DB/logs" \
  -H "Content-Type: application/json" \
  -d '{"level":"error","message":"price mismatch","source":"client"}'

Expiry monitor for a continuing prototype

The app is still useful after day one, and you need a simple way to decide whether to extend or migrate.

Checking __meta makes lifecycle visible without adding end-user expiry banners to the product UI.

  • Expired databases return 410 but remain restorable for 72 hours.
  • Ask before creating a replacement database because silent replacement loses the old data.
  • Store the admin URL and expires_at in private developer handoff notes.
curl "$TMPSTATE_DB/__meta"

# Example policy for an agent or maintenance script:
# - if expires_in < 6 hours and demo is scheduled, ask whether to extend
# - if tier is free and real users entered data, export before expiry
# - if prototype becomes daily-use software, propose Pro or migration