Updating System App Schemas
How to update fixed system application schemas safely.
Fixed system applications (admin, profiles, metahubs, applications) use manifest-driven schema definitions stored in TypeScript source files. This guide explains how to modify their database schemas safely and what happens at runtime when the platform detects a change.
Prerequisites
Understanding of the monorepo package structure.
Familiarity with
SystemAppDefinitionmanifests in backend packages.Read System App Migration Lifecycle for the architectural context behind these steps.
Where Definitions Live
Each fixed system app has a SystemAppDefinition manifest in its backend package:
admin
packages/admin-backend/base/src/platform/systemAppDefinition.ts
profiles
packages/profile-backend/base/src/platform/systemAppDefinition.ts
metahubs
packages/metahubs-backend/base/src/platform/systemAppDefinition.ts
applications
packages/applications-backend/base/src/platform/systemAppDefinition.ts
The manifest declares business tables, fields, data types, and capabilities. It is the single source of truth for the schema shape of each system app.
Step-by-Step: Adding a New Field
Open the manifest file for the target system app.
Find the
targetBusinessTablesarray (or the sharedconstit references).Add a new field entry to the appropriate table definition:
Build the package to verify the TypeScript compiles:
Run a full workspace build to propagate changes:
Start the server. The platform will detect the change and apply it automatically during
initDatabase().
Step-by-Step: Adding a New Business Table
Add a new entry to the
targetBusinessTablesarray withkind,codename,tableName,fields, and optionalpresentation.Follow the naming convention:
cat_for catalogs,doc_for documents,rel_for relations,cfg_for settings.If the new table has foreign key references to other tables in the same system app, set
targetTableCodenameon theREFfield.Build and restart the server as described above.
Step-by-Step: Removing a Field or Table
Removing fields or tables produces destructive changes. The diff engine will generate DROP_COLUMN or DROP_TABLE entries and apply them automatically because the system app upgrade path passes confirmedDestructive: true.
Remove the field or table from
targetBusinessTables.Build and restart. The platform will drop the corresponding column or table.
Make sure no application code references the removed column before deploying.
What Happens at Runtime
When the server starts, ensureRegisteredSystemAppSchemaGenerationPlans() runs for each application_like system app:
First boot (no tables exist): Full schema generation via
SchemaGenerator.generateFullSchema(). A baseline migration is recorded in<schema>._app_migrations.Subsequent boot (tables match): The latest snapshot is read from
_app_migrations. A diff is calculated against the current manifest. If no changes exist, the boot continues without writing anything.Schema changed in manifest: The diff engine detects additive and destructive changes.
SchemaMigrator.applyAllChanges()applies only the computed DDL changes (not a full recreate). A new migration record is written to_app_migrationswithsnapshotBefore,snapshotAfter, and the list of changes.
Where Migrations Are Stored
Migrations are stored in the database, not in files. Each system app schema contains a _app_migrations table:
Each row contains:
name
Migration name (e.g. baseline_admin_structure_0_1_0)
meta
JSONB with snapshotBefore, snapshotAfter, changes, summary
publication_snapshot
Always null for system apps
_upl_created_at
Timestamp of when the migration was applied
SQL Migration Files vs. Manifest-Driven Changes
System apps may also have SQL migration entries in their migrations array (the kind: 'sql' entries). These are separate from the manifest-driven schema generation and serve a different purpose:
pre_schema_generationSQL migrations run beforeSchemaGeneratorcreates tables. They typically create the schema itself or set up helper functions.post_schema_generationSQL migrations run after tables exist. They add indexes, RLS policies, seed data, or database functions.Manifest-driven changes (via
targetBusinessTables) are handled entirely by the diff engine and recorded in_app_migrations.
CLI Commands
The migration CLI provides commands for inspecting and managing system app schemas:
Common Mistakes
Forgetting to build before restart. The server loads compiled JS from
dist/. If you change the TypeScript source but skip the build step, the old manifest is used and no migration runs.Referencing a removed column in application code. The diff engine will drop it, but queries that still use it will fail at runtime.
Mismatched
currentBusinessTablesandtargetBusinessTables. Keep them in sync unless you are intentionally modeling a migration from one shape to another.
Related Pages
Last updated