Skip to main content

kysera schema

PostgreSQL schema management commands for multi-tenant and modular database architectures.

:::info PostgreSQL Only Schema management commands are only available for PostgreSQL databases. SQLite has no schema support, and MySQL uses databases as the equivalent concept. :::

Commands

CommandDescription
listList all database schemas
createCreate a new database schema
dropDrop a database schema
infoShow detailed information about a schema
cloneClone a schema structure to a new schema
compareCompare two schemas and show differences

list

List all database schemas.

kysera schema list

Options:

--json Output as JSON
--tenant Only show tenant schemas (tenant_* pattern)
-v, --verbose Show detailed information (tables, owner, size)
-c, --config <path> Path to configuration file

Examples:

# List all schemas
kysera schema list

# Show detailed information
kysera schema list --verbose

# Only tenant schemas
kysera schema list --tenant

# JSON output
kysera schema list --json

Output:

Database Schemas

[tenant] tenant_acme
[tenant] tenant_globex
public
auth
admin

Use --verbose for detailed information

Verbose Output:

Database Schemas

name tables owner size tenant
─────────────────────────────────────────────────────
public 12 postgres 4.25 MB -
auth 5 postgres 1.12 MB -
tenant_acme 15 app_user 2.34 MB acme
tenant_globex 15 app_user 1.87 MB globex

create

Create a new database schema.

kysera schema create <name>

Arguments:

  • name - Schema name to create

Options:

--tenant <id> Create as tenant schema with specified ID
--if-not-exists Do not error if schema already exists
--force Skip confirmation prompt
-v, --verbose Show detailed output
-c, --config <path> Path to configuration file

Examples:

# Create a schema
kysera schema create auth

# Create without confirmation
kysera schema create auth --force

# Create tenant schema (uses tenant_<id> naming convention)
kysera schema create --tenant acme
# Creates schema: tenant_acme

# Create if not exists
kysera schema create auth --if-not-exists

Tenant Schema Creation:

When using --tenant, the schema name follows the tenant_<id> naming convention:

kysera schema create --tenant 123
# Output:
# Schema 'tenant_123' created successfully
#
# Tenant schema created with naming convention:
# Schema: tenant_123
# Tenant ID: 123
#
# Next steps:
# 1. Run migrations in the new schema:
# kysera migrate up --schema tenant_123
# 2. Or clone from a template schema:
# kysera schema clone template tenant_123

drop

Drop a database schema.

kysera schema drop <name>

Arguments:

  • name - Schema name to drop

Options:

--cascade Drop all objects in the schema (CASCADE)
--if-exists Do not error if schema does not exist
--force Skip confirmation prompt
-v, --verbose Show detailed output
-c, --config <path> Path to configuration file

Protected Schemas:

The following schemas cannot be dropped:

  • public
  • pg_catalog
  • information_schema

Examples:

# Drop a schema (requires confirmation)
kysera schema drop old_tenant

# Drop with CASCADE (drops all contained objects)
kysera schema drop old_tenant --cascade

# Drop without confirmation
kysera schema drop old_tenant --cascade --force

# Drop if exists (no error if not found)
kysera schema drop old_tenant --if-exists

Confirmation Output:

Warning: You are about to drop schema 'tenant_old'
Tables: 15
Size: 2.34 MB
CASCADE: All objects in the schema will be dropped!

? Are you sure you want to drop schema 'tenant_old'? (y/N)

info

Show detailed information about a schema.

kysera schema info <name>

Arguments:

  • name - Schema name

Options:

--json Output as JSON
--indexes Show index information
--foreign-keys Show foreign key relationships
-v, --verbose Show all details (indexes, foreign keys)
-c, --config <path> Path to configuration file

Examples:

# Basic info
kysera schema info public

# With indexes
kysera schema info public --indexes

# With foreign keys
kysera schema info public --foreign-keys

# Full details
kysera schema info public --verbose

# JSON output
kysera schema info public --json

Output:

Schema: public
──────────────────────────────────────────────────

General Information:
Owner: postgres
Tables: 12
Size: 4.25 MB

Tables:
- users
- posts
- comments
- categories
- tags
- post_tags

Commands:
Clone: kysera schema clone public <target>
Drop: kysera schema drop public --cascade
Compare: kysera schema compare public <other>

Verbose Output (with --verbose):

Schema: tenant_acme
──────────────────────────────────────────────────

General Information:
Owner: app_user
Tables: 15
Size: 2.34 MB

Tenant Information:
Tenant ID: acme
Schema Pattern: tenant_<id>

Tables:
- users
- posts
- comments
...

Indexes:
table index type unique primary columns
────────────────────────────────────────────────────────────────
users users_pkey btree Yes Yes id
users users_email_idx btree Yes No email
posts posts_pkey btree Yes Yes id
posts posts_user_id_idx btree No No user_id

Foreign Keys:
constraint table column references onDelete onUpdate
──────────────────────────────────────────────────────────────────────────────────────
posts_user_id_fkey posts user_id public.users.id CASCADE CASCADE
comments_post_fkey comments post_id public.posts.id CASCADE CASCADE

clone

Clone a schema's structure (and optionally data) to a new schema.

kysera schema clone <source> <target>

Arguments:

  • source - Source schema name
  • target - Target schema name

Options:

--include-data Include table data in the clone
--exclude <tables...> Tables to exclude from cloning
--tenant <id> Create target as tenant schema with specified ID
--force Skip confirmation prompt
-v, --verbose Show detailed output
-c, --config <path> Path to configuration file

Examples:

# Clone structure only
kysera schema clone template new_tenant

# Clone with data
kysera schema clone template new_tenant --include-data

# Clone as tenant schema
kysera schema clone template --tenant acme
# Creates: tenant_acme

# Exclude certain tables
kysera schema clone template new_tenant --exclude logs sessions

# Without confirmation
kysera schema clone template new_tenant --force

Confirmation Output:

Clone Schema
──────────────────────────────────────────────────
Source: template
Target: tenant_acme
Tables: 15
Size: 1.24 MB
Include Data: No

? Clone schema 'template' to 'tenant_acme'? (Y/n)

Success Output:

Schema 'template' cloned to 'tenant_acme' successfully

Clone Summary:
Tables cloned: 15
Size: 48 KB

Tenant schema created:
Schema: tenant_acme
Tenant ID: acme

Next steps:
View schema: kysera schema info tenant_acme
Run queries: kysera query --schema tenant_acme

compare

Compare two schemas and show differences.

kysera schema compare <schema1> <schema2>

Arguments:

  • schema1 - First schema name
  • schema2 - Second schema name

Options:

--json Output as JSON
-v, --verbose Show detailed output (list all common tables)
-c, --config <path> Path to configuration file

Examples:

# Compare two schemas
kysera schema compare template tenant_acme

# Verbose output
kysera schema compare template tenant_acme --verbose

# JSON output
kysera schema compare template tenant_acme --json

Output:

Schema Comparison
============================================================

Schema Overview:

template tenant_acme
------------------------------ ------------------------------
Tables: 15 Tables: 16
Size: 1.24 MB Size: 2.34 MB

Tables only in 'template' (1):
- archived_users

Tables only in 'tenant_acme' (2):
+ custom_settings
+ tenant_config

Common tables (14):
users, posts, comments, categories, tags ... and 9 more

------------------------------------------------------------

Summary:
Total tables in 'template': 15
Total tables in 'tenant_acme': 16
Common tables: 14
Unique to 'template': 1
Unique to 'tenant_acme': 2

Hints:
To sync missing tables, run migrations on 'tenant_acme'

Identical Schemas Output:

Schema Comparison
============================================================

Schema Overview:

template tenant_new
------------------------------ ------------------------------
Tables: 15 Tables: 15
Size: 1.24 MB Size: 48 KB

Schemas have identical table structures

------------------------------------------------------------

Summary:
Total tables in 'template': 15
Total tables in 'tenant_new': 15
Common tables: 15
Unique to 'template': 0
Unique to 'tenant_new': 0

Multi-Tenant Workflow

Initial Setup

  1. Create a template schema:

    # Create template schema
    kysera schema create template

    # Run migrations in template
    kysera migrate up --schema template
  2. Provision new tenants:

    # Clone template for new tenant
    kysera schema clone template --tenant acme

    # Or create and migrate separately
    kysera schema create --tenant acme
    kysera migrate up --schema tenant_acme
  3. Manage tenant schemas:

    # List all tenant schemas
    kysera schema list --tenant

    # Check schema drift
    kysera schema compare template tenant_acme

    # Remove old tenant
    kysera schema drop tenant_old --cascade --force

Schema Migration Strategy

When updating the database structure:

# 1. Update template first
kysera migrate up --schema template

# 2. Check differences with existing tenants
kysera schema compare template tenant_acme

# 3. Run migrations on each tenant
kysera migrate up --schema tenant_acme
kysera migrate up --schema tenant_globex

Automated Tenant Provisioning

#!/bin/bash
TENANT_ID=$1

# Create tenant schema from template
kysera schema clone template --tenant "$TENANT_ID" --force

# Verify schema
kysera schema info "tenant_$TENANT_ID"

echo "Tenant $TENANT_ID provisioned successfully"

Configuration

Schema in Configuration File

You can set a default schema in kysera.config.ts:

import { defineConfig } from '@kysera/cli'

export default defineConfig({
database: {
dialect: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD,
schema: 'public' // Default schema
}
})

Environment-Based Schema

export default defineConfig({
database: {
// ...
schema: process.env.DB_SCHEMA || 'public'
}
})