Sapporta
Build Database Applications in TypeScript
- Editable data grids
- Generated REST APIs
- Forms and reports
- Row-scoped auth
- Agent-ready OpenAPI
- Integrated stack
Build Database Applications in TypeScript
export const booksTable = sqliteTable("books", {
id: integer("id").primaryKey({ autoIncrement: true }),
title: text("title").notNull(),
author: text("author").notNull(),
});
export const books = sapportaTable({
drizzle: booksTable,
meta: {
label: "Books",
rowScope: "systemGlobal",
rowLabelColumns: ["title"],
search: { columns: ["author", "title"] },
children: [
{
table: "quotes",
foreignKey: "book_id",
label: "Quotes",
columns: ["quote_text"],
defaultSort: "-created_at",
},
],
columns: {
title: { width: 64 },
author: { width: 36 },
},
},
}); Sapporta helps you build applications that operate mostly on relational data - like personal databases, operational tools, and business software.
npx sapporta init to production.
Just like creating a new spreadsheet, in Sapporta, once you define a table, you immediately get a live editable grid. The grid brings a similar set of features and UX as that of AirTable, NocoDB and similar collaborative databases. It is filterable, sortable, and searchable. They also support nested master-detail relationships, and full keyboard support that work across levels.
The APIs used by the grid are bounded by row-level security and CASL authorization, and so it can be shipped directly to end users without additional hardening.
There is no inversion of control - Sapporta projects are regular Hono APIs + React single page applications that your codebase owns completely. There are no opaque abstractions that obscures control-flow or hide the underlying implementation.
Sapporta projects are structured for coding agents. The generated codebase follows explicit conventions for project structure. These conventions cover data modeling, API behavior, UI flows, access rules, and migrations. Sapporta also ships sapporta-skills for table creation, reports, row access, custom endpoints, and frontend workflows. The legible-code skill applies domain types, module boundaries, and the functional core, imperative shell design to agent-authored TypeScript.
Every table exposes auto-generated REST endpoints (list, create, update etc.), that adhere to row-level security and CASL authorization policies.
APIs don't require manual JSON
wiring for request/response
parsing. Both the
auto-generated APIs, as well
as any domain APIs you write,
are all defined using ts-rest.
The definitions go into packages/shared, and are immediately
available in the frontend as
typed functions.
Applications that you build with Sapporta can be operated fully agentically by default. Agents can insert new rows, or update existing rows, map freeform data into database entries, execute multi-step workflows, run reports, and answer data questions, all through the same APIs used by the application UI.
For example, if you built an Expense Tracking system with Sapporta, then its users can scribble reimbursement details - rough vendor names, relative dates, all in a freeform structure - into an LLM agent, and have it be recorded accurately in the database. This requires no additional code (but the user needs to have the Sapporta skill installed).
This works because Sapporta
publishes the application's
API surface at
/api/openapi.json. It has detailed route
descriptions, usage examples,
and documented request
parameters for every available
endpoint. This includes all
the automatically generated
table APIs as well as any
domain specific APIs written
by you.
It is also secure by default - since all endpoints are secured by row-security and CASL, the agents only have access to the exact data that the user themselves have.