Platform
Hosting & deploysPush to a branch, get a live linkDatabaseReal Postgres, one per appSign-inEmail and Google, users includedFile storageUploads, photos and documentsPaymentsCheckout and plans, same projectAI gatewayEvery model, with a hard spend cap
Solutions
Moving an app overFrom Lovable, Bolt, Replit or v0Your first real appNever shipped anything beforeSide project → businessWhen people start paying youPricingFrom $25/month plus prepaid credits
Resources
DocsGuides written for people who are newFor AI agentsClaude Code, Cursor, MCP, JSON CLIChangelogWhat shipped, most recent firstComponentsPaste-ready UI for sign-in and moreSupportA person answers, not a queue botRefer a friendBoth of you get credits Get started
Open in ChatGPT Open in Claude Last updated 2026-10-07

Key-value store

plax.kv — get, set and expire keys in your app's own database. Nothing to add, nothing to paste.

Every app collects small shared state — a last-run timestamp, a cached sidebar, a rate-limit counter, a flag you flip without a deploy. plax.kv keeps it in one table in your app's own database, created the first time you touch it. No service to add, no key to paste.

Set and get

import { createPlaxKv } from "@camplax/plax";
import { db } from "./db.ts";

const kv = createPlaxKv(db);

await kv.set("digest:last", { sentAt: Date.now() });
const last = await kv.get<{ sentAt: number }>("digest:last"); // { sentAt: … } | null

get answers null when the key is not there; values are any JSON — objects, arrays, strings, numbers. getMany reads a batch in one round trip and delete says whether a row existed:

await kv.getMany(["digest:last", "digest:count"]); // { "digest:last": { sentAt: … } }
await kv.delete("digest:last"); // true — and false the second time

Expiry

ttlSeconds on set gives a key a lifetime — once it lapses the key reads as absent, and the expired row is deleted the next time it is touched, so stale keys clean themselves:

await kv.set("lock:digest", runId, { ttlSeconds: 300 });

Expiry is measured on the database clock, not your app's. A set without ttlSeconds never expires, and setting the same key again replaces the whole row — TTL included.

Listing and counting

await kv.list({ prefix: "lock:" });        // key names, unexpired only
await kv.increment("digest:count");        // atomic — adds 1 by default
await kv.increment("digest:bytes", bytes); // or any amount

list takes { prefix?, limit? } — limit defaults to 1,000 and caps there. increment is one statement (INSERT … ON CONFLICT DO UPDATE … RETURNING): an absent or expired key starts at the amount, a live number adds to it, and anything else throws not_a_number — counters and rate limits with no read-modify-write race. One edge to know: an expired key that increment revives restarts without its old TTL — set the TTL again with set if it should still expire.

The rails

Keys are at most 256 characters and serialised values at most 64 KB. Past either — or a bad key, an unserialisable value, a non-positive TTL — the call throws a PlaxKvError with a code (key_too_long, value_too_large, invalid_key, invalid_value, invalid_ttl, not_a_number) before the database is touched, so a 40 MB blob can never land in the one table the whole app shares.

What it is — and is not

This is your app's own Postgres, and it behaves like it: reads see committed writes immediately, and handed a transaction client it joins your transactions — exactly what you want for correctness-sensitive state like idempotency records, "did we send this" flags, or a lock with an expiry. The bound is your database pool.

It is not a distributed cache: there is no sub-millisecond edge read, no memory-tier eviction, and no point filling it with megabytes of churn. If you need that, you need a real cache — this does not pretend to be one.

Under the hood: one plax_kv table — key text primary key, value jsonb, expires_at timestamptz, updated_at timestamptz — created lazily on first call. There is no migration to write and nothing on Camplax's side to provision.

© 2026 Camplax From $25 / month · credits stop at zero
Ask about Camplax