1. Project Overview & Quickstart (teableio/teable)
File: README.md
Quick Guide
- Want to see it in action? Try Teable on our hosted cloud at teable.ai.
- Deploying it yourself? Start from the Deployment guide — standalone in minutes, or the full AI platform on your own machines.
Four platforms in one
AI chat, App Builder, and app deployments are part of Teable, not bolt-ons.
Self-hosting Teable deploys four platforms in one:
- A secure, scalable agent sandbox — every AI session runs in its own isolated container, started on demand and gone when the session ends.
- A resource-efficient app deployment platform — every app your team builds and publishes runs as its own lightweight, long-lived container.
- An AI workflow engine — automations triggered by record changes, schedules, and webhooks, with AI steps, running right where your data lives.
- A full-featured database collaboration platform on PostgreSQL — tables, views, permissions, and API.
Self-hosting wraps your own compute into an agent-ready, fully controlled
productivity environment — putting AI in the hands of everyone on your team.
See Architecture for how the
pieces fit together.
✨Features
🤖 AI-native
- AI Chat — ask, analyze, and operate on your data in natural language.
- App Builder — describe the app you need; an agent builds it in an isolated sandbox and deploys it to its own URL in one click.
- AI automations — react to record changes, schedules, and webhooks, with AI steps doing the reasoning.
- AI field filling — generate and enrich field values in bulk.
🍺 Everything you need, out of the box
Grid, Form, Kanban, Gallery, and Calendar views; formulas, field conversion,
filtering, grouping, sorting, and aggregation; comments, record history,
undo/redo, and real-time collaboration; attachments preview, batch editing,
import/export, search, validation, charts, plugins, SQL query — and millions
of rows without breaking a sweat.
More features land all the time — see the Changelog.
Deploy
Three ways to run Teable — pick by what you need (full comparison in the
Deployment guide):
| Teable Cloud | Full-featured self-host | Standalone self-host | |
|---|---|---|---|
| Tables, collaboration, API, automation | ✅ | ✅ | ✅ |
| AI features (chat, agents) | ✅ | ✅ | ❌ |
| App Builder (build & deploy apps) | ✅ | ✅ | ❌ |
| Start here | teable.ai | teableio/teable-deployment | Docker guide |
Prefer not to deploy by hand? Hand teableio/teable-deployment
to your AI agent (Claude Code, Codex, …) together with a domain, a DNS token,
and access to the target machine or cluster — it can take the deployment end
to end.
Already running standalone Teable? The runtime plane installs next to it and
your data stays in place: migration guide.
Development
1. Initialize
# Enabling the Help Management Package Manager
corepack enable
# Install project dependencies
pnpm install2. Initialize Postgres
make switch-db-mode3. Custom Environment Variables(Optional)
cd apps/nextjs-app
cp .env.development .env.development.local4. Run Dev Server
you just need to start backend, it will start next server for frontend automatically, file change will be auto reload
cd apps/nestjs-backend
pnpm devWhy Teable?
Software should fit the work — not the other way around. Every team knows
the compromise: you have a concrete business need, you search for an app, and
it covers 70% of it. So you bend your process around the tool, or you file a
ticket and wait quarters for custom development. The work adapts to the
software, when it should be the software adapting to the work.
AI only changes this if it reaches the real workflow. A wave of AI tools
can now generate an app on demand — but each one is built in a vacuum: an
empty database, no knowledge of how your team actually operates, one more
island to integrate and maintain. The demo impresses; the workflow stays the
same.
Teable puts the agent where the work already happens. Your data, apps,
automations, permissions, and collaborators live here, on a real PostgreSQL —
and the AI operates all of it. Chat acts on live data. Automations reason
over records as they change. App Builder ships working systems wired to the
same database your team already uses — so what gets built is never another
silo, but a precise extension of the workflow it came from. AI lands in the
work seamlessly, and what it builds fits the business exactly.
And it's an architecture an IT team can say yes to. Self-hosting Teable
packages your own compute into an agent-ready platform for the whole team:
every AI session confined to an isolated sandbox, every app shipped as a
standard container, everything resting on PostgreSQL and HTTP APIs, with your
own models and keys, on your own infrastructure. Full AI capability, full
control — neither traded for the other.
The result is a different way of building: business systems shaped precisely
to your business, delivered in days instead of quarters, evolving as fast as
the work itself — by everyone on the team, not just the people who code.
License
This repository is the Teable Community Edition: free for self-hosting under
the AGPL-3.0 license (packages under packages/ are MIT). See
./LICENSE for details.
The official image ghcr.io/teableio/teable
ships the complete product: free to run, with paid plans unlocking AI features
and advanced enterprise capabilities in place — activate with a license
key, no image swap, no migration. See pricing and
subscribe & activate.
File: apps/nextjs-app/README.md
The web-app
You don't need start this app when developing locally, it's started by the nestjs-backend.
all env is maintained in the .env* file, it is shared with the backend.
File: apps/nestjs-backend/src/features/calculation/README.md
我有这样的一个数据结构定义,id 为 fieldId, dependency 是当前 field 所依赖的 field 的 id。
IFieldMap 包括了所有 field 对应的信息的 map
interface ITopologicalItem {
id: string;
dependencies: string[];
}
interface IField {
type: "other" | "link";
}
type IFieldMap = { [id: string]: IField };一个 record 中的某个 field 变化了之后,就会触发相关的依赖计算。也就是说,我们提供一个 recordId 然后按照拓扑排序中的顺序,依次计算每个 field 的值。
link 字段在这里会导致 一个 record 变化,引发其他多个 record 变化的情况,比如
recordA1 和 recordA2 的 fieldY 都引用 recordB1 的 fieldX 的值,其中 fieldY 为 type 为 link 的字段
recordA1[fieldBLinkB] = recordB1[fieldB];
recordA2[fieldBLinkB] = recordB1[fieldB];那么,当 recordB1 的 fieldX 变化的时候,就会触发 recordA1 和 recordA2 的 fieldY 的变化。
也就是说,当传入 recordB1 的 id 进入拓扑排序遍历的时候,经过 fieldY 字段时,需要裂变成 recordA1、recordA2 两个 id,然后继续遍历计算
请问,我如何一次性查询出,从 recordB1 出发,最终会发生变化的 record, 以及他们与各自 field 之间的关系
简单场景
b1[fieldB] 变化
fieldB -> fieldLinkB
[
{ id: "fieldB", dependencies: [], recordId: ["b1"] },
{ id: "fieldLinkB", dependencies: ["fieldB"], recordId: ["b1"], targetRecordId: ["a1", "a2"] }, // formula({fieldB})
];link 字段的计算,是带入关联表中的 recordId 以及对应的 recordData 计算的,而计算后的值,将存入当前表的 targetRecordId 中对应的 link 字段下。
[
{ id: "fieldB", dependencies: [], recordId: ["b1"] },
{ id: "fieldLinkB", dependencies: ["fieldB"], recordId: ["b1"], targetRecordId: ["a1", "a2"] },
{ id: "fieldA", dependencies: ["fieldLinkB"], recordId: ["a1", "a2"] },
{ id: "fieldLinkA", dependencies: ["fieldA"], recordId: ["a1", "a2"], targetRecordId: ["b1"] },
];fieldA -> FieldLinkA -> formula。
拓扑排序只包含从变更入口开始的有向图
[
{ id: "fieldA", dependencies: [], recordId: ["a1"] },
// { id: "fieldC", dependencies: [] },
{ id: "FieldLinkA", dependencies: ["fieldA"], recordId: ["a1", "a2"], targetRecordId: ["b1"] },
// { id: "FieldLinkC", dependencies: ['fieldC'], recordId: ['c1', 'c2'], targetRecordId: ['b1'] },
{ id: "formula", dependencies: ["FieldLinkA", "FieldLinkC"] },
];单次找出所有 recordId
我有以下数据结构, 这是一组拓扑排序后的数据结果。代表着 fieldA -> fieldLinkA -> fieldLinkB 这样的一张有向无环图关系。
tableName 代表表名称
fieldName 代表字段名称
targetLinkField 代表外键的字段名称,该字段存储了关联的 record
linkedTable 代表了这个 targetLinkField 关联的表名称
dependencies 代表关联后,他们需要查询 record 中哪个 field 的值
const topologicalOrder = [
{ tableName: "A", fieldName: "fieldA", dependencies: [] },
{
tableName: "B",
fieldName: "fieldLinkA",
targetLinkField: "__fk_fieldLinkA",
linkedTable: "A",
dependencies: ["fieldA"],
},
{
tableName: "C",
fieldName: "fieldLinkB",
targetLinkField: "__fk_fieldLinkB",
linkedTable: "B",
dependencies: ["fieldLinkA"],
},
];table 中的 record 数据用 json 表达如下
A 表
[
{ id: "idA1", fieldA: "A1" },
{ id: "idA2", fieldA: "A2" },
];B 表
[
{ id: "idB1", fieldB: "B1", fieldLinkA: "A1", __fk_fieldLinkA: "idA1" },
{ id: "idB2", fieldB: "B2", fieldLinkA: "A1", __fk_fieldLinkA: "idA1" },
];C 表
[
{ id: "idC1", fieldC: "C1", fieldLinkB: "A1", __fk_fieldLinkB: "idB1" },
{ id: "idC2", fieldC: "C2", fieldLinkB: "A1", __fk_fieldLinkB: "idB1" },
{ id: "idC3", fieldC: "C3", fieldLinkB: "A1", __fk_fieldLinkB: "idB2" },
];根据上述表述,如果 'idA1' 中的 fieldA 发生了变化,既可以推导出 ['idB1', 'idB2'] 和 ['idC1', 'idC2', 'idC3'] 都会受到关联关系的影响发生变化
我如何通过 SQL 一次性查找出这些 recordId 呢? 为了实现动态的查询需求,我会将 idA1 和 topologicalOrder 作为参数传入
一对多关系,反向引用计算链
A 表
{ __id: 'idA1', fieldA: 'A1', oneToManyB: ['C1,C2', 'C3'] }B 表
{ __id: 'idB1', fieldB: 'C1,C2', manyToOneA: 'A1', __fk_manyToOneA: 'idA1', oneToManyC: ['C1', 'C2'] }
{ __id: 'idB2', fieldB: 'C3', manyToOneA: 'A1', __fk_manyToOneA: 'idA1', oneToManyC: ['C3'] }C 表
{ __id: 'idC1', fieldC: 'C1', manyToOneB: 'C1,C2', __fk_manyToOneB: 'idB1' },
{ __id: 'idC2', fieldC: 'C2', manyToOneB: 'C1,C2', __fk_manyToOneB: 'idB1' },
{ __id: 'idC3', fieldC: 'C3', manyToOneB: 'C3', __fk_manyToOneB: 'idB2' },引用关系拓扑排序
const topoOrder = [
{
dbTableName: "B",
fieldName: "oneToManyC",
foreignKeyField: "__fk_manyToOneB",
relationship: Relationship.OneMany,
linkedTable: "C",
},
{
dbTableName: "A",
fieldName: "oneToManyB",
foreignKeyField: "__fk_manyToOneA",
relationship: Relationship.OneMany,
linkedTable: "B",
},
{
dbTableName: "C",
fieldName: "manyToOneB",
foreignKeyField: "__fk_manyToOneB",
relationship: Relationship.ManyOne,
linkedTable: "B",
},
];我们看到。
A 表中的 idA1.oneToManyB: ['C1,C2', 'C3'] 的值,是从 C 表中id 为 'idB1', 'idB2' 对应的 fieldB 字段中得到,并形成数组的。
同理:
B 表中
idB1.oneToManyC: ['C1', 'C2'] 的值,是从 C 表中id 为 'idC1', 'idC2' 对应的 fieldC 字段中得到,并形成数组的。
idB2.oneToManyC: ['C3'] 的值,是从 C 表中id 为 'idC3' 对应的 fieldC 字段中得到,并形成数组的。
C 表中
idC1.manyToOneB: 'C1,C2' 的值,是从 B 表中id 为 'idB1' 对应的 fieldB 字段中引用得到。
idC2.manyToOneB: 'C1,C2' 的值,是从 B 表中__id 为 'idB1' 对应的 fieldB 字段中引用得到。
问题 1
C.idC1.fieldC 的值发生了变化,从 C1 变成了 C11, 怎么更新 A 表和 B 表的对应值?
首先我们用循环遍历的代码来实现,先定义一下最终的输出结构,者个结构由后续参与计算的时候,如何进行多值 lookup 合并来决定。
什么是多值 lookup 呢? 比如 B.oneToManyC 的值是 ['C1', 'C2'],那么我们需要从 C 表中找到 __id 为 'idC1', 'idC2' 的记录,然后将他们的 fieldC 字段的值合并成一个数组,最终得到 ['C1', 'C2']。
问题 2
假设,我们现在 A.oneToManyB 以及 B.oneToManyC 中的值都为空。我们怎么通过 topoOrder 中给出的关系,利用 SQL 查询加上 ts 计算,得到他们的值呢?
File: apps/nestjs-backend/README.md
NestJS backend for teable
TODO:
remove @valibot/to-json-schema in ai-sdk6
remove effect in ai-sdk6
remove @ai-sdk/provider-utils in ai-sdk6
File: dockers/examples/standalone/README.md
Example with teable standalone
Standalone runs the Teable core: tables, collaboration, API and automation.
For the AI features (chat, App Builder, sandboxes) use the
full-featured self-host deployment --
and if you are already running standalone, your data stays in place when
upgrading.
Look into the .env file and update the vaiables before executing docker compose up -d.
Teable
- Accessible via
http://127.0.0.1:3000 - Uses postgres db for storage
- Telemetry is disabled
File: dockers/examples/docker-swarm/README.md
Teable Deployment with Docker Swarm
This guide provides step-by-step instructions on how to deploy Teable using Docker Swarm, including initializing the
swarm, deploying services, and managing updates and service removal.
Initialization
First, initialize your Docker Swarm environment:
docker swarm init
# Or specify the advertise address manually if needed:
# docker swarm init --advertise-addr 192.168.99.100Deploying Services
Use the provided deploy.sh script to deploy your services. You can specify the service type and stack name as
arguments:
# Syntax: ./deploy.sh [service_type] [stack_name]
./deploy.sh - example
# To view the current services:
docker service ls
# To deploy and configure updates for your application:
./deploy.sh app exampleManaging Services
Removing a Service
To remove a deployed service, use the following command:
docker stack rm exampleRolling Updates
For rolling updates of the application, forcing a re-deployment of the current configuration:
docker service update --force example_teableAdditional Notes
Cleaning Up Shutdown Containers
To clean up containers that are in a shutdown state:
docker rm $(docker stack ps --no-trunc -f "desired-state=shutdown" --format "{{.Name}}.{{.ID}}" example)This guide outlines the basic steps for deploying and managing your Teable application with Docker Swarm, including
service deployment, updates, and cleanup procedures.
File: dockers/examples/cluster/README.md
Setup Instructions for Teable Cluster
Before executing docker compose up -d, ensure to update the variables in the .env file according to your
environment's requirements.
Teable Configuration
- Access URL: Access the Teable interface via http://127.0.0.1:80.
- Database Storage: Utilizes PostgreSQL database for data storage.
- Telemetry: Telemetry collection is disabled by default.
MinIO Endpoint Configuration
When configuring the Teable cluster to use MinIO for storage, it's necessary to replace the
placeholder <MINIO_ENDPOINT> in the .env file with the appropriate endpoint:
For Intranet Use: Replace
<MINIO_ENDPOINT>with the IP address of the host where MinIO is deployed. This setup
is recommended if MinIO and Teable are on the same network.For Extranet Use: Replace
<MINIO_ENDPOINT>with the domain name or extranet IP address of your MinIO instance.
Use this configuration if you need to access MinIO from outside your local network.
Public Database Proxy Configuration
To ensure smooth native database connections, you need to set the PUBLIC_DATABASE_PROXY variable in the .env file to
its default value, 127.0.0.1:42345. This port should match the port specified for the teable-db container in
the docker-compose.yaml file's ports attribute and can be adjusted to suit your needs.
Important Note: When using ports 80 or 443, it's essential to explicitly specify the port number in the URL.
Failing to do so is not allowed. This practice guarantees accurate address resolution and dependable connectivity,
providing a solid foundation for your database connections.
Ensure to review and adjust these configurations to match your deployment environment before starting the Teable
cluster.
2. Official Technical Reference & Guides (teableio/teable-docs)
Teable Help
Development
Install the Mintlify CLI to preview the documentation changes locally. To install, use the following command
npm i mintlifyRun the following command at the root of your documentation (where docs.json is)
mintlify devPaste Image
Use the Paste Image extension to paste images into the markdown file.
Open mdx file and press cmd + option + v to paste image.
Image Assets
Images are shared repo assets. Keep paths stable, names searchable, and
rendering consistent.
When a change includes images, ask the agent to read this section first and
commit only images processed according to these rules.
Folder Rules
- Use
images/{top-level-group}/{topic}/. - Top-level groups:
docs,selfhosted,api,security,changelog. docsmaps to the Help Center Docs tab. Do not flattenimages/docs/{topic}/intoimages/{topic}/.{topic}maps to the navigation name indocs.json, such asai,base,space,sso,authority-matrix,get-id,sql-query, oractivate.- Do not put new screenshots in the
images/root. - Do not commit random staging folders.
- Put unreferenced images in
images/orphans/YYYY-MM-DD/...first. Do not
delete them directly. - Docs pages must not reference
images/orphans/....
Naming
Use this format:
images/{top-level-group}/{topic}/YYYY-MM-DD-page-slug-purpose.png
images/{top-level-group}/{topic}/YYYY-MM-DD-page-slug-purpose.webp
images/{top-level-group}/{topic}/YYYY-MM-DD-page-slug-purpose-zh.png- Date = day the image enters the docs repo.
- Page slug = docs page or feature area.
- Purpose = visible UI or action. Do not use
image1or random hashes. - Use
.pngfor static UI screenshots. - Prefer
.webpfor animated screenshots. - English-only screenshot = shared asset. Do not add
-en. - Chinese-specific screenshot = add
-zh.
Image Handling
- Logos, inline icons, external button images, videos, and historical changelog
media do not need screenshot styling.
MDX Usage
Use explicit `` for docs screenshots:
- Use
className="docs-screenshot"for new PNG/WebP docs screenshots underimages/docs/,images/api/,images/selfhosted/, andimages/security/. - Alt text must be non-empty.
- Alt text describes the visible UI or action. Do not use
screenshotor the
file name. - Do not use `` for new docs screenshots.
- Avoid
width="400", percentage widths, and one-off image sizing. - Changelog images keep their historical display. Add useful alt text when
context exists. Do not adddocs-screenshotblindly.
Publishing Changes
Push to origin main branch, the changes will be deployed to production automatically.
Troubleshooting
- Mintlify dev isn't running - Run
mintlify installit'll re-install dependencies. - Page loads as a 404 - Make sure you are running in a folder with docs.json