Self-hosted service
apitable/apitable avatar
apitable/apitable

APITable is a spreadsheet that refuses to pretend it is a spreadsheet

🚀🎉📚 APITable, an API-oriented low-code platform for building collaborative apps and better than all other Airtable open-source alternatives.

15,635 stars1,426 forksTypeScriptAGPL-3.0

At a glance

What is it?
A close look at the AGPL low-code database behind APITable: canvas rendering, operational transform sync, the Changeset and Operation model, and what the compose topology tells you about self hosting.
Who is it for?
APITable is at its best when one person needs a working internal tool this afternoon and a second person needs to write to the same rows by HTTP tomorrow. The canvas renderer, the Changeset and Operation history, and the space-scoped permission model are all built for that pairing.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 31 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A canvas grid backed by an actual database

The first thing worth understanding about APITable is that the spreadsheet is a rendering strategy, not the storage model. The README describes a canvas rendering engine behind the grid, and it pairs that with what it calls a database native architecture: Changeset, Operation, Action, Snapshot. That combination matters, because a plain canvas draws fast but has no notion of what changed, and a plain grid of DOM nodes has no cheap way to repaint sixty thousand rows. By keeping every cell edit as an operation in a changeset, the renderer has something to replay and the API has something to serve.

The scale claim in the docs is 100k+ data rows with real-time collaboration, which is an order of magnitude past what most people expect a spreadsheet to hold. The canvas engine is the reason that number is plausible rather than marketing, though the README does not describe the rendering strategy in enough detail to say whether it virtualizes rows, batches paint requests, or does both. The docs settle the question of what the UI is built on and leave the paint strategy to the source.

The rest of the data model is ordinary and well named. Tables, columns, and rows support create, read, update, and delete, and fields can be sorted, filtered, grouped, hidden, and resized. A datasheet is only one of several projections over the same data. The README counts seven view types and then lists grid, gallery, mindmap, kanban, Gantt, and calendar, which is six names. Nobody is hiding anything, it reads like a seventh view shipped after the list was written, and it is a small reminder to read feature counts in these repos as approximate.

Operational transform rather than last write wins

Realtime collaboration here is described with a specific algorithm rather than a vague promise of live editing: operational transform, abbreviated OT in the README. That choice is a design position. Last write wins needs a lock or a merge strategy and loses data when two people type in the same cell at the same moment. Operational transform rewrites concurrent operations so both intents survive, at the cost of maintaining a transform function between every pair of operations that can overlap.

Most tools that claim live collaboration quietly mean the second option. Naming OT means the project has accepted the harder path, and the room server in the compose file is the piece that pays for it. That service carries `ENABLE_SOCKET=true`, a raised old space size of 2048 megabytes, and an 80000 byte header limit, plus `API_MAX_MODIFY_RECORD_COUNTS` defaulting to 30, which reads as a per request ceiling on batch record writes. Holding operation history and resolving concurrency in memory is what those numbers are for.

yaml
      - API_MAX_MODIFY_RECORD_COUNTS=${API_MAX_MODIFY_RECORD_COUNTS:-30}
      - INSTANCE_MAX_MEMORY=4096M
      - ENABLE_SOCKET=true
    networks:
      - apitable
    depends_on:
      mysql:
        condition: service_healthy

The compose file also exposes a run of ports on the room server, 3333, 3334, 3001, 3002, 3005, and 3006, which is a wider surface than a single websocket listener needs. Without the internal service documentation it is guesswork which of those are HTTP and which are collaboration channels, so the honest reading is that this is a multi role node service whose internals the repository root does not expose.

Spaces instead of bases, and links that do not stop at one table

The structural decision that separates APITable from the Airtable shape it is usually compared against is the space. The README is explicit that APITable uses separated workspaces in place of an app or base based structure, and that this is what makes unlimited table linking possible. In a base per application model, two tables can only relate to each other if they happen to live together, which makes cross project relations a copy operation. Spaces invert that: the workspace is the unit of isolation, and tables inside it can be related freely.

Link columns come in one direction and bidirectional forms, and the docs describe the behavior as infinite cross links. Paired with the Changeset model, that gives a relation graph where a record in one datasheet can point at records in several others and stay consistent under concurrent edits. This is the part of the product that most closely resembles a real application data layer rather than a spreadsheet convention, and it is the reason teams tend to keep their schemas in APITable after the first prototype.

Permissions ride on the same structure. There are row and column level permissions, and a feature the docs call `Mirror` turns a view into a mirror so that row level access can be enforced through a filtered projection rather than by rewriting every query. That is a small idea with real consequences: the permission boundary stays attached to the view, so different audiences can see different slices of one table without duplicating data.

yaml
version: "3.9"

services:
  web-server:
    image: ${IMAGE_REGISTRY}/${IMAGE_WEB_SERVER}
    pull_policy: ${IMAGE_PULL_POLICY:-if_not_present}
    restart: always
    expose:
      - "8080"

Six services and one ordering rule in the compose file

The deployment story is written down in `docker-compose.yaml`, and it is more informative than the feature list. The file declares a web server on 8080, an image proxy, a backend server on 8081, a room server for collaboration, a databus service, MySQL, and an init container. Every service pulls from an `IMAGE_REGISTRY` prefix with a shared pull policy and mounts configuration through `env_file`, defaulting to `.env`.

The single ordering rule is the interesting part. The backend server does not start until init db has exited successfully, expressed as a dependency on a completed init container rather than a running database. That is the correct shape for a migration job: a health check on MySQL only proves the database accepts connections, while a completed init container proves the schema was applied. The v1.14.0-beta changelog lists a fix for an init db service that did not complete successfully, which suggests this seam is exactly where deployments go wrong.

yaml
    depends_on:
      init-db:
        condition: service_completed_successfully
    healthcheck:
      test: ["CMD-SHELL", "curl -sS 'http://localhost:8081' || exit 1"]
      interval: 5s
      timeout: 5s
      start_period: 30s
      retries: 60

The backend health check allows a thirty second start period and sixty retries, so a cold start has roughly five minutes before the container is judged unhealthy. On a modest machine the Java backend is the slow one here. The root Makefile layers compose files by environment, with separate files for a data environment and a development environment, and uses `docker buildx bake` for multi image builds, so day to day work runs through one compose entry point rather than a matrix of commands.

An nx monorepo where the datasheet is its own package

The tree is pnpm plus nx: `pnpm-workspace.yaml`, `nx.json`, `build.js`, and a `packages/` directory, with `backend-server/`, `gateway/`, and `init-db/` sitting beside it as separate concerns. The package scripts show the intended startup surface, and the naming is a good map of the architecture.

json
  "scripts": {
    "preinstall": "npx only-allow pnpm",
    "sc": "npm run start:core",
    "sd": "npm run start:datasheet",
    "sr": "npm run start:room-server",
    "build": "nx run-many -t build",
    "build:web": "nx run @apitable/datasheet:build",

`@apitable/core` is the shared state and query layer, `@apitable/datasheet` is the grid frontend that uses it, and `@apitable/room-server` is the collaboration node. There is also `@apitable/widget-sdk`, `@apitable/components`, `@apitable/api-client`, `@apitable/i18n-lang`, and an `air-agent` package for the assistant work. Builds are ordered rather than free running: datasheet has a pre step that builds i18n, core, icons, components, ai, and widget sdk before the datasheet bundle, because the grid depends on all of them. `preinstall` refuses anything but pnpm, which matters for anyone following a generic npm tutorial and wondering why the install fails.

The two language stacks are split by role rather than by preference. TypeScript with NextJS and NestJS handles the frontend and the collaboration surface, and Java with Spring Boot handles the backend server. A `crowdin.yml` at the root plus localized READMEs for French, Spanish, German, Simplified Chinese, Traditional Chinese, and Japanese confirm that translation is treated as a first class workflow rather than a community patch.

Extensibility, licensing, and the cost of the beta tag

Two things constrain how you use this project, and neither is in the feature list. The first is the license. APITable ships under AGPL-3.0, which is the strongest copyleft in common use. Self hosting it for a company's own internal tools is fine. Embedding a modified version into a product you hand to customers is a different question, because the network use clause reaches users interacting with it over a network. Anyone considering the shareable and embeddable pages as a customer facing surface needs to read that clause rather than assume the permissive licenses of the tools it replaces.

The second is release maturity. The three published tags are `v1.14.0-beta`, `v1.13.0-beta.1`, and `v1.13.0-beta`, so the project has shipped beta tags since December 2024 with one point release in between, in May 2025. The v1.14.0-beta notes are mostly documentation edits, an init db completion fix, an all in one script fix, and a hosted sync, which reads like stabilization rather than new capability. The repository is not archived and the default branch is `develop`, with the last recorded push on 2026-09-06.

Extensibility sits in three places: the widget system with more than twenty official open source widgets, customizable data column types and formulas, and customizable automation robot actions. The robot automation plus BI dashboard plus auto generated form combination is what turns a datasheet into an internal tool, and the listed integrations with n8n, Zapier, and Appsmith cover the trigger side. The widget SDK is the part most likely to matter long term, because custom widgets are how teams embed domain specific reading and writing experiences without forking the grid.

Editorial conclusion

APITable is at its best when one person needs a working internal tool this afternoon and a second person needs to write to the same rows by HTTP tomorrow. The canvas renderer, the Changeset and Operation history, and the space-scoped permission model are all built for that pairing. Its rough edges are the ones that come with a TypeScript and Java monorepo still on a beta tag, plus an AGPL-3.0 grant that shapes how you embed it. Start from the compose file, because the service order in it explains the rest of the architecture better than any feature list does.

Frequently asked questions

What is APITable used for?

APITable is an API-oriented low-code platform for building collaborative database applications, positioned as an open-source alternative to Airtable. The repository describes it as covering everything from personal projects to enterprise internal tools, with datasheets, multiple view types, forms, dashboards, and automation.

How do you self-host APITable?

The repository ships a `docker-compose.yaml` with a web server, backend server, room server, databus, MySQL, and an init container, all configured through an `.env` file and built with `docker buildx bake`. The README points at the Installation section for the full walkthrough and offers a one-click deploy through Dome.

What stack is APITable built on?

Two stacks by role: TypeScript with NextJS and NestJS for the frontend and collaboration surface, and Java with Spring Boot for the backend server. The repository is a pnpm and nx monorepo holding packages such as `@apitable/core`, `@apitable/datasheet`, and `@apitable/room-server`.

Is APITable open source?

Yes, under the AGPL-3.0 license. That is a strong copyleft with a network use clause, so internal self hosting is straightforward while embedding a modified build into a customer facing product deserves a closer read of the license terms.

Official sources

  1. apitable/apitable on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/apitable-apitable.svg)](https://hysenlabs.com/projects/apitable-apitable)