Build an Airtable alternative
Build an Airtable alternative as typed fields and a table view. Linked records, formulas, and live views are the other product. Here is the split.
You can build an Airtable alternative as one workflow. Typed fields, a table, a CSV you can preview before it writes. The catalog calls Airtable that, not a blank database builder.
You pay $24–$54 a person a month. The card says that price scales with the team. The verdict is “Build the workflow, not Airtable”.
Earlier today I wrote about a QR Tiger alternative. That was a regular backlog post. This one is a teardown: what is public about the architecture, which half you can vibe-code, and which half you should leave alone.
Build an Airtable alternative as one workflow
The Airtable page splits the work into three rows.
The first row is 3 days. Typed fields, a table view, CSV import. That is a schema you wrote once, a grid, and a dry-run importer. You can vibe-code this.
The second row is 2 weeks and up. Kanban views, comments, attachments, simple automations. Still one process. More screens, a file in R2, a webhook that fires once.
The third row is 3 months and up. A schema designer, formulas, linked bases, an extension platform. Skip that row.
If you tell an agent to “clone Airtable”, it aims at the third row. The useful v1 is the first one.
Here is the shape of the data. Nothing else has to exist at 3 days.
const shot = {
id: 'shot-184',
title: 'Kitchen scene, take 3',
status: 'in-progress',
owner: 'flavio',
dueOn: '2026-10-24',
minutes: 45,
}
A record, a handful of types, a status that every surface must obey. Linked records can wait. Formulas wait until you are tempted to compute across tables.
The audience line on the page is an operations team tracking a single repeatable process. If that is you, a fixed workflow is enough. If the company already lives in a shared base with rollups and Interfaces, keep paying.
The base is their source of truth, the same way Linear is for issues. How Linear works is that split.
The same v1 also replaces Baserow and Grist if what you wanted was typed rows and a table.
How does Airtable work
Howie Liu told Software Engineering Daily in 2019 that Airtable is a relational database with a spreadsheet interface. I am summarizing that interview and the engineering posts below, not reconstructing their database.
They prototyped with a MySQL table per user table. Create Contacts, and they ran CREATE TABLE.
A formula reads other fields. A linked record is a foreign key they do not call a foreign key. A lookup or a rollup sits on the other side of that link. Walk that graph with a query per cell, and one keystroke pays disk and network a hundred times. So they built their own engine and keep a base in memory.
Andrew Kumansley’s Rewriting Our Database in Rust (March 6, 2026) is the current public picture. A worker loads the whole base. Requests route to that process so one customer’s runaway formula does not starve another base. The old engine was TypeScript. After three years they shipped a Rust in-memory database as a Node native extension: one write thread, a pool of read threads, a dispatcher.
A write must tell every live query how it changed, without re-running the query. Incremental view maintenance produces a compact diff, and the client applies it. NOW() and some column config changes can rewrite most of a column in one shot, so they built batch updates.
Treat “every production base still has exactly one dedicated worker” as an educated guess past that March post. Jayden Navarro’s Supporting Realtime Query Updates (September 2026) already moved query-update generation onto a realtime thread, with an IVM engine inspired by DBSP. The worker still owns the base. Order is assigned at persist time. The client gets a diff for the query it is looking at.
A 3-day v1 does not need any of that. One D1 database and a version number on each row is enough. Two edits of the same shot resolve to one winner.
Linked records are bidirectional
Airtable publishes a REST API at https://api.airtable.com/v0. I am summarizing their field model and list records docs, not their storage format.
You list a table with GET /v0/{baseId}/{tableIdOrName}. They tell you to use the table id, so a rename does not break the URL. Records look like rec560UJdUtocSouk. Empty cells are omitted. Pages are 100 rows.
A linked record field is multipleRecordLinks. The cell is an array of record ids. You write ids, not names.
linkedTableId is the other table. inverseLinkFieldId is the field that points back. prefersSingleRecordLink is a UI preference. Their docs are explicit: the API, copy-paste, and programmatic updates can still attach more than one record.
Linking records is the product rule. Create a link from Shots to Scenes, and Airtable creates the backlink on Scenes. Edit either side, the other side moves. Convert a text column into a link, and missing names become new records in the other table.
A vibe-coded workflow needs none of those endpoints. Copy the product rule only if you have two tables. Most v1s do not. Put the scene title on the shot and stop.
If you do add a link later, write both sides in one transaction. A shot that points at a scene whose shotIds forgot it is a bug you will chase for a week.
async function linkShotToScene(db, { shotId, sceneId }) {
return db.transaction(async (tx) => {
const shot = await tx.get('shots', shotId)
const scene = await tx.get('scenes', sceneId)
if (!shot || !scene) return { ok: false }
await tx.setLink('shots', shotId, 'sceneId', sceneId)
await tx.addToList('scenes', sceneId, 'shotIds', shotId)
return { ok: true }
})
}
That is two updates, not Airtable’s engine. Kill the request after the first write and you still have one consistent pair, or nothing.
Lookups and rollups sit on top of the backlink. The API type for a lookup is multipleLookupValues. It names recordLinkFieldId and fieldIdInLinkedTable. A rollup adds a formula over those values.
Change a contact, and an account rollup may have to move. That graph is why they left MySQL.
The 3-day row has no lookups. If your wiki is already a CRM with rollups, you are starting the Notion rebuild from the other side. How Notion databases work already sent that work here.
The formula engine is the other product
Keyhan Vakil’s Stateful Model-Based Database Fuzzing (February 16, 2022) named the Recalculator. It reads the writes in a transaction and decides which formulas, filters, and sorts have to move. A change to First Name recomputes Name and the views that filter on Name, and leaves the rest alone. They fuzzed it because a filter on a formula is only as honest as that graph.
I will not claim the Recalculator is still a TypeScript object with that name. The 2026 IVM work is the public successor. A formula is still a column, and NOW() ticks for every row.
Airtable’s formula function list is the language. NOW() returns the current date and time. It recalculates when the formula runs, when a base loads, or roughly every 15 minutes while the base is open. Closed bases tick about every hour, and only if a time-dependent automation or a sync depends on it.
The API stores the formula with field ids, {fldXXXXXXXXXXXXXX}, plus referencedFieldIds. filterByFormula on list-records runs that language per row.
A 3-day v1 does not evaluate user formulas. Derive the few values you need in code. Overdue is dueOn < today && status !== 'done'. Write that next to the validator, not in a string the user edits.
The mini-prompt on the page is “Typed fields without a schema designer”. One list of keys and types. One validate function. The table, the detail form, the CSV import, and any later webhook all call it.
function validateShot(input) {
const statuses = ['todo', 'in-progress', 'done']
if (!statuses.includes(input.status)) {
return { ok: false, field: 'status', value: input.status }
}
const minutes = Number(input.minutes)
if (!Number.isFinite(minutes) || minutes < 0) {
return { ok: false, field: 'minutes', value: input.minutes }
}
return {
ok: true,
shot: {
title: String(input.title).trim(),
status: input.status,
owner: input.owner,
dueOn: input.dueOn,
minutes,
},
}
}
Change the allowed statuses in that list, and every surface follows. That is the 3-day schema. A designer that lets anyone add a formula column is the 3-month row.
Chris Pederkoff’s Instant Schema Changes (July 13, 2026) is why adding a column feels cheap: a schema version on each row, so add and drop are metadata. You do not need that if the schema lives in code.
Views that do not fork the data
A view in Airtable is a filter, a sort, and a layout over the same records.
The 2022 fuzzing post called views “filtered and sorted copies of the original dataset”. Read that as computed membership, not a forked table. Shared views and the “when a record enters this view” automation both depend on that membership being right.
The list-records view parameter makes the contract public. Pass a view name or id, and you get those records in the view’s order, unless you also pass sort. Fields hidden in the view still come back. Hide a column, the data is still on the record.
Shrey Banga’s The Curious Case of the Missing Cell (May 13, 2022) is the client side. Before View Projection, a client loaded a whole table. After it, the contract is the view, a slice of cells. Unhide a field, and the backend backfills those cells before the client may read them.
Treat “clients still subscribe per view with that 2022 protocol” as an educated guess. The kanban is the same rows. Drag a shot to Done, and the grid row moves, because both surfaces call the same transition.
That is the second mini-prompt: “One status service to rule the kanban, the table, and the form”. The drag handler maps a column to a status and calls the same function the detail form uses. Illegal moves return the same error.
A 3-day v1 ships one table view. A 2-week v1 can add a kanban that groups the same rows by status. Do not copy the rows into a board table. Trello is the other way around, $10–$17.5 a person a month, three hours for columns and cards.
The CSV importer is the other 3-day piece. Dry-run first. Map columns, preview rows, list every error with the source line number. Persist nothing until you confirm. The mini-prompt is “The import that changes nothing until you say so”.
An Airtable alternative for one process
I would rebuild Airtable for a four-person ops team that currently tracks one pipeline in a spreadsheet they email around. Status, owner, due date, a table, a CSV import with a preview. One validate function.
I would host it next to the product, on Astro, D1, and R2, because that is the stack on the card. Use Rails if that is your weekday language. Add a kanban that groups the same rows once people ask to drag. I would not build a formula language, a schema designer, linked bases, or an extension store.
If the rent is an internal admin on top of data you already have, that is Retool, $12–$65 a person a month, four days for the tools you run. If twelve coworkers already treat the shared base as the record, keep paying.
Start with the first row
Go to Build your own Airtable. Copy the AHA prompt, or the stack you already write.
Tell the agent the first estimate only: typed fields, table view, CSV import. Three days.
If the company already lives in the formula column, keep paying.