Build features
Database
Every app gets its own PostgreSQL database that no other app can see.
Each app has its own PostgreSQL schema and its own database role. The role can only see its own schema, so an app can never read another app's tables, even in the same workspace. There is nothing to set up: the database is created on the app's first deploy.
Create tables with migrations
Put SQL files in a migrations/ (or db/migrations/) folder. Name them so they sort in order:
my-app/
├── migrations/
│ ├── 0001_init.sql
│ └── 0002_add_region.sql
└── worker.tscreate table leads (
id bigint generated always as identity primary key,
name text not null,
email text,
stage text not null default 'new',
created_by text not null,
created_at timestamptz not null default now()
);On each deploy, Inhaus runs only the migrations it has not run before, in name order. They run as the app's own role, in the app's schema, so table names need no prefix. If one fails, the deploy fails with migration_failed and the previous version stays live.
Reading and writing from your app
env.INHAUS_DB_SCHEMA holds the name of the app's schema.
Query from your AI tool
Your AI tool can read and change the data directly with db_query. Good for checking what is stored, fixing a bad row, or loading data.
How many leads are in the lead tracker, by stage?Mark every lead from acme-old.com as "lost". Show me which rows first.- Each call runs one SQL statement. Pass values as parameters (
$1,$2), never by pasting them into the SQL. readmode runs in a read-only transaction.writemode changes data and is recorded in the access log. Your AI tool should ask you before using it.- Editors and the Owner can use both modes.
The Data tab
The app's Data tab in the dashboard shows the database size and every table with its row count.
Limits
| Limit | |
|---|---|
Rows returned by one db_query call | 1,000 |
Time per statement from db_query | 10 seconds |