brain/wiki/data-storage-observability/tables.md
A built-in relational database inside Activepieces: users store structured data (typed columns, rows) without an external DB, edited in a spreadsheet-like UI and wired into flows. Available in CE, EE, and Cloud.
TEXT, NUMBER, DATE, STATIC_DROPDOWN. Field limit AP_MAX_FIELDS_PER_TABLE (default 100), enforced by field.validateCount({ insertCount }).position ASC, created ASC, and table.exportTable() follows the same order.RECORD_CREATED, RECORD_UPDATED, RECORD_DELETED.table.service.ts, field.service.ts, record.service.ts, record-side-effects.ts.recordSideEffects.handleRecordsEvent() finds matching TableWebhooks and triggers their linked flows with the record as payload.packages/pieces/core/tables/) gives flows triggers (New/Updated/Deleted Record) and actions (Create/Get/Find/Update/Delete Record, Clear Table), calling the internal API with a Bearer token.READ_TABLE / WRITE_TABLE via securityAccess.project(...). VIEWER gets read only. ENGINE/SERVICE principals skip the role check.POST /v1/fields/reorder takes { tableId, fieldIds } (the full ordered id list the client already holds) and resequences positions to 0..n-1 in a single UPDATE … unnest(fieldIds) WITH ORDINALITY scoped by projectId + tableId, so foreign or stale ids are no-ops. Creates default to MAX(position)+1 (append); imports pass the source array index so order never depends on insert timing. In the UI this is react-data-grid native column dragging (draggable columns + onColumnsReorder).'', so NEQ/NOT_EXISTS match unset columns.record.create() bulk insert caps at 50 per batch, transactional.permission arg to securityAccess.project(...) is required — passing undefined silently allows any project member.field.validateCount() check alone races on bulk paths: all concurrent creates read the same pre-save count and pass. Bulk/import paths (table.create with fields, project-state/project-replace apply) MUST call it once up front with the batch size before their Promise.all.FieldState carries no position, so array order only applies to newly created fields.cell.fieldIndex references, so it must remap every record's cells when fields move.Entry point: tablesModule, registered in packages/server/api/src/app/app.ts and mounting the three controllers under /v1/tables, /v1/fields, /v1/records.
packages/server/api/src/app/tables/tables.module.ts — module registration and route prefixespackages/server/api/src/app/tables/table/ — table service (CRUD, export, webhook management), controller, Table and TableWebhook entitiespackages/server/api/src/app/tables/field/ — field service, controller, Field entitypackages/server/api/src/app/tables/record/ — record service (CRUD, bulk ops), controller, Record and Cell entities, record side effects that fire TableWebhook flowspackages/core/shared/src/lib/automation/tables/ — shared schemas for Table, Field, Record, Cell, TableWebhook, plus request/response DTOspackages/web/src/app/routes/tables/id/index.tsx — the table editor page, react-data-grid basedpackages/web/src/features/tables/ — editor components, React Query hooks, client/server state stores, API callspackages/pieces/core/tables/ — the Tables piece: triggers and actions flows use to read and write tablesPaths verified 2026-07-17.