Leaderboards
Rank every player, live — from the events your game emits and the state it only stores.
players?order=wins.descLive tables, an API and a change feed, built from everything your contract wrote — not just the events it announced.
Events, resources and the entries inside a SmartTable, folded into state your frontend can query and your backend can subscribe to. On the Aptos infrastructure you already know.
on(sold, (s) => {const row = sellers.row(s.seller)row.sold += 1row.revenue += s.price - s.fee})
225 - 5 → revenue 220
| seller | sold | revenue |
|---|---|---|
| 0x7a3f…c41d | 1 | 515 |
| 0x1e87…8d2a | 1 | 705 |
| 0x9b02…4f77 | 1 | 894 |
| 0x6146…e554 | 1 | 220 |
One row per seller, queryable over REST.
Aptos already provides the infrastructure for reading and indexing on-chain data: RPC for point reads, the hosted Indexer for common assets, the Transaction Stream for everything a transaction touched.
Turning that into the backend your frontend needs is still application logic, state management, database work and server code. And a contract announces less than it stores. It emits events for what it expected you to want, and keeps the rest in storage because storage is what it needs to work: the SmartTable of open positions, the running sum it keeps for itself, the vote tally no event is fired for.
Read only the events and that half is invisible — not missing, invisible, which is worse, because what you do get still looks plausible.
So it gets assembled the same way in every project: a processor, a database, a server, a deploy pipeline. A week of work, and something to maintain forever. Nineveh simplifies the layer in the middle.
Aptos already provides the infrastructure for accessing chain data and building custom indexing pipelines. Nineveh sits one layer above it. Instead of every application developer assembling the same backend logic around those primitives, Nineveh gives you a simpler way to define and serve application-specific state.
Web developers have familiar abstractions for building application backends. Nineveh brings that simplicity to Aptos applications, while keeping Aptos as the source of truth.
Your contract is the source of truth. A reducer says what to do when something arrives: when this event lands or this resource changes, that row changes. No processor to write, no migrations, no schema to keep in step.
export const sellers = table({key: { seller: address },columns: { sold: u64.default(0), revenue: u64.default(0) },})on(sold, (s) => {const row = sellers.row(s.seller)row.sold += 1row.revenue += s.price - s.fee})
That is the whole language: on, a row, an assignment, delete, if and return. It reads like TypeScript, but nothing is executed: it compiles to a fold that replays the same way every time.
GET /v1/tables/sellers?order=revenue.desc&limit=2{ "rows": [{ "seller": "0x7a3f…c41d","sold": "128","revenue": "94210" },{ "seller": "0x1e87…8d2a","sold": "91","revenue": "63880" }],"count": 4 }
Wide integers come back as strings, because a u128 doesn't fit a JavaScript number. Every table gets the same treatment.
A query answers the first question, and REST over your tables is how you ask it. But a frontend that has to stay current can only keep asking: on a timer, diffing what comes back, guessing at the interval.
The change feed turns that around. Every row a reducer writes leaves the same commit as an event, in commit order, down a connection you already have open. Your app finds out because something happened, not because it checked.
Last-Event-ID and carries on. Nothing missed, nothing twice.Describe the tables your product needs, with their keys, columns and types, rather than building an indexing pipeline from scratch.
Not just events: resource changes and the entries inside a table, read from the write set — including the state your contract never emitted an event about.
Fold that activity into application-level state: counters, totals, relationships, computed columns.
Query the result over REST, subscribe to changes as they commit, or receive signed webhooks.
Edit a reducer and the table it writes is wrong by definition: every row was folded by the old rule. Nineveh rebuilds it beside the live one and swaps the two when the new one catches up.
The rebuild replays Nineveh's own record log rather than the chain, so most rule changes don't mean buying the same history twice. The old table answers reads throughout, frozen at the version it had reached, and starts advancing again once the new one is in.
A balance that goes 5 → 12 → 7 lands as 7, never as 12.
Rows and cursor commit together, or neither does.
A restart resumes from the cursor. Nothing counted twice.
Move's integers, checked. A bad rule halts; it never corrupts.
One rule holds the whole thing up: a reducer is the only thing that ever writes a table. Not your API, not a script, not a migration. Everything below is a consequence of it, and none of it is available to a backend that lets anything else write.
Events and write sets. The source of truth, on chain.
What arrived, in version order, kept so it can be replayed.
Your fold. The only thing that writes a table.
Rows, typed and queryable. Derived, never written to.
REST, a live feed, signed webhooks. Reads only.
The two ends are yours. Everything between them is the part you would otherwise build.
Studio creates the project, then shows you what it built: the tables you chose, the ones you folded yourself, every change as it commits, and whether the whole thing is keeping up. These are real screens against a contract running on devnet, not mockups — the rows in them came off the chain.

DeFi, marketplaces, games, social and consumer apps, agents, dashboards. The shapes differ; the need doesn't. Each one wants application state derived from what its contracts do, queryable the way the product actually asks for it.
Rank every player, live — from the events your game emits and the state it only stores.
players?order=wins.descWhat's for sale right now, and what each seller has made.
listings?order=price.ascEvery holder, sortable, with the history behind each balance.
holders?order=balance.descAppend-only history you can page through, by author or by time.
posts?author=0x7a3f…Volume, fees and exposure: totals the contract never stored.
daily_volume?order=day.descWhatever your contract emits, you can fold it into a table shaped like the question you actually ask. None of these need a contract change.
Not a trial. The free tier doesn't expire, doesn't need a card, and stays free when paid plans arrive. Testnet and devnet for as long as you want them. The limits are real numbers Nineveh enforces, and when you reach one it tells you which and what it means.
Billing isn't built yet. When it is, everything in the free tier stays free. Paid is what you add, not what you move to.
Start with Aptos. Let Nineveh handle the application layer. Backfills, cursors, retries and crash recovery included.
Nineveh reads the modules off the chain. Tick the events, resources and tables to follow.
A few lines fold them into rows: counters, totals, relationships, whatever your product needs.
REST and a live change feed over tables you designed.
t.me/+kVwq6suLvZNlNGE0