Skip to main content

Workspace CLI

The informer-workspace command manages your app's local development database (workspace). It creates an isolated Postgres schema on the Informer server where your migrations/*.sql files are applied.

Setup

Add these scripts to your package.json (added automatically by informer-init):

{
"scripts": {
"workspace:init": "informer-workspace init",
"workspace:migrate": "informer-workspace migrate",
"workspace:reset": "informer-workspace reset"
}
}

All workspace commands accept --mode <name> to target a specific environment (see Environment Variables):

# Initialize workspace on test server (loads .env.test)
npm run workspace:init -- --mode test

Commands

workspace:init

Creates a new workspace datasource and runs all migrations:

npm run workspace:init

This command:

  1. Creates a workspace datasource named {package-name}-dev on the Informer server
  2. Creates a _migrations tracking table in the workspace schema
  3. Runs all .sql files in migrations/ in alphabetical order
  4. Saves INFORMER_DEV_WORKSPACE={naturalId} to the active env file (.env by default, or .env.<mode> when using --mode)

You typically run this once when setting up a new project. After that, the Vite plugin auto-runs pending migrations on npm run dev.

workspace:migrate

Runs any pending migrations that haven't been applied yet:

npm run workspace:migrate

This is useful when you've added new migration files and want to apply them without restarting the dev server.

workspace:reset

Drops all tables in the workspace and re-runs all migrations from scratch:

npm run workspace:reset

This gives you a clean database. Use it when:

  • Your migrations have gotten out of sync
  • You want to test migrations from a clean slate
  • You've modified an existing migration file (not recommended in production, but useful during development)
caution

workspace:reset drops all tables in the dev workspace, including any data you've inserted. This is irreversible.

How It Works

The workspace is a regular Informer datasource of type workspace. It gets its own Postgres schema, isolated from other tenants and apps.

Migration Tracking

The _migrations table tracks which migrations have been applied:

CREATE TABLE _migrations (
name TEXT PRIMARY KEY,
executed_at TIMESTAMPTZ DEFAULT NOW()
);

When you run migrations, the system:

  1. Reads all .sql files from migrations/ (sorted alphabetically)
  2. Queries _migrations for already-applied names
  3. Runs only the pending ones, in order
  4. Records each successful migration in _migrations

Auto-Provisioning

When you run npm run dev with a migrations/ directory present, the Vite plugin automatically:

  1. Checks if INFORMER_DEV_WORKSPACE exists in .env
  2. If it does, verifies the datasource still exists on the server and runs pending migrations
  3. If it doesn't (or the datasource was deleted), recreates the workspace automatically

This means you rarely need to run workspace:init manually — the dev server handles it, including recovering from deleted workspaces.

Dev vs Production Workspaces

Dev WorkspaceProduction Workspace
Created byworkspace:init or auto-provisioned_deploy endpoint on deploy
Schema nameDerived from datasource IDDerived from app ID + tenant
Migration sourceLocal migrations/ filesFiles uploaded to app library
Accessed viaquery() in server handlersquery() in server handlers
DataTest/dev dataReal production data

The dev workspace is completely separate from the production workspace. Deploying your app doesn't affect your dev data, and vice versa.

Writing Migrations

Migration files are plain SQL in the migrations/ directory:

migrations/
001-create-orders.sql
002-add-status.sql
003-add-indexes.sql

Rules

  • Alphabetical naming — Use numeric prefixes (001-, 002-, etc.) to control execution order
  • Append-only — Never modify a migration that has been deployed. Add a new file instead.
  • Standard SQL — Use any Postgres-compatible SQL (DDL, DML, functions, etc.)
  • One transaction per file — Each migration runs in its own transaction. If it fails, that migration is rolled back but previous successes are preserved.

Example

-- migrations/001-create-orders.sql
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer TEXT NOT NULL,
total NUMERIC(10,2) DEFAULT 0,
status TEXT DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- migrations/002-add-line-items.sql
CREATE TABLE line_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
product TEXT NOT NULL,
quantity INTEGER DEFAULT 1,
price NUMERIC(10,2) NOT NULL
);

CREATE INDEX line_items_order_idx ON line_items (order_id);