STATEWAY
Documents at the edge. Guarantees you can name.
A Durable Object owns each partition — so conditional writes and
counters are real. Talk to it with native /v1, or keep
boto3 and Cosmos when you must.
One object. One partition. No home region.
Stateway runs on Cloudflare Workers and Durable Objects. Schema in a catalog, data in SQLite per partition — not a single-region cluster you fan out to later.
- Strong where it matters
- Inside a partition key: serialised requests, conditional writes, atomic counters, local indexes, single-partition transactions.
- Honest where it ends
- Global indexes and full-collection scans are eventually consistent. Cross-partition atomicity is two-phase commit — or you stay in one partition.
- Same bytes, three wires
-
Native JSON, DynamoDB, and Cosmos DB share one store. Write with
/v1, read with an SDK — or the other way around.
Where partition-strong wins.
Pick a partition key that matches how you race: a tenant, a user, a room, an account. Inside that key, reads and writes are serialised. Across keys, stay eventual or pay for two-phase commit.
- Multi-tenant product state
-
Partition on
tenantId. Config, entitlements, and feature flags update withif_version— no lost write when two admins save at once. - Edge sessions and preferences
- Keep per-user documents next to the Worker that serves them. Conditional puts and TTL cover login, cart drafts, and prefs without a home-region round trip.
- Counters, quotas, and balances
- Atomic increments live inside the partition. Rate limits, seat counts, and wallet-style balances stay correct under contention without a separate lock service.
- Orders and workflows per account
-
Hash on the customer (or order aggregate). Single-partition
transactions cover place / cancel / inventory adjust as one
atomic unit; cross-account moves use
/v1/transact. - Realtime rooms and game state
- One Durable Object per room or match. Presence, scores, and turn state are strongly consistent for everyone keyed to that room — without inventing your own coordinator.
- Move Dynamo or Cosmos to the edge
-
Keep boto3 or
@azure/cosmospointed atapi.stateway.dev. Same documents as native/v1, so you can migrate clients one path at a time.
Poor fit: analytics scans across every tenant, or workloads that need a strongly consistent global secondary index on every write. Prefer a warehouse or a regional store for those.
What /v1 already covers
Collections, items, query and scan, indexes with real
CREATING→ACTIVE status, streams, TTL,
batch, and single- or multi-collection transactions.
POST /v1/collections— create with keys, TTL, streamPUT …/items— put, update, delete withif_versionPOST …/query·…/scan— including parallel segmentsPOST …/indexes·GET /v1/streams·POST /v1/transact
Full reference in the
getting started guide
and repo docs/native-api.md.
Three ways in. One document.
Start on native /v1. Keep
boto3 or @azure/cosmos when an existing client already
speaks them — endpoint
https://api.stateway.dev.
import { Stateway } from "https://stateway.dev/client.js";
const sw = new Stateway({
endpoint: "https://api.stateway.dev",
token: process.env.STATEWAY_TOKEN,
});
await sw.createCollection({ name: "orders", hash: "pk", range: "sk" });
await sw.put("orders", { pk: "acme", sk: "o#1", total: 42.5 });
const { item } = await sw.get("orders", { pk: "acme", sk: "o#1" });
import boto3
ddb = boto3.client(
"dynamodb",
endpoint_url="https://api.stateway.dev",
region_name="us-east-1",
aws_access_key_id=STATEWAY_AKID,
aws_secret_access_key=STATEWAY_SECRET,
)
ddb.put_item(
TableName="Orders",
Item={
"customerId": {"S": "acme"},
"orderId": {"S": "o#1"},
"total": {"N": "42.5"},
},
)
import { CosmosClient } from "@azure/cosmos";
const client = new CosmosClient({
endpoint: "https://api.stateway.dev",
key: process.env.COSMOS_MASTER_KEY,
});
const { database } = await client.databases.createIfNotExists({ id: "shop" });
const { container } = await database.containers.createIfNotExists({
id: "orders",
partitionKey: { paths: ["/customerId"] },
});
await container.items.create({
id: "ord-1",
customerId: "acme",
total: 42.5,
});
Invite-only. Production already up.
Hosted Stateway is not a public signup. If you have credentials, set
STATEWAY_TOKEN to your secret and hit
/v1. Health and latency budgets live at
api.stateway.dev/health.