Schema
Define Postgres tables and indexes in your app-local Gonvex backend folder.
Schema
Gonvex schemas are written in Go and live in your app-local gonvex/ folder.
my-app/
gonvex/
schema.go
_generated/
The schema describes the tables, columns, and indexes your project expects in Postgres.
Example
package backend
import "github.com/gonvex/gonvex/pkg/gonvex"
func Schema(s *gonvex.Schema) {
s.Table("tasks", func(t *gonvex.Table) {
t.ID("id")
t.String("title")
t.Text("description", gonvex.Nullable)
t.String("status")
t.String("priority", gonvex.Nullable)
t.Time("created_at")
t.Time("updated_at", gonvex.Nullable)
t.Index("by_status", "status")
t.Index("by_created_at", "created_at")
t.Index("by_status_created_at", "status", "created_at")
})
}
Column Types
The schema API covers common Postgres-backed application data:
t.ID("id")
t.String("title")
t.Text("body", gonvex.Nullable)
t.Int("count")
t.Int64("size", gonvex.Nullable)
t.Float64("price")
t.Bool("published")
t.Time("created_at")
t.JSON("metadata", gonvex.Nullable)
Indexes
Indexes are declared near the table they support:
t.Index("by_status", "status")
t.Index("by_tenant_created_at", "tenant_id", "created_at")
t.UniqueIndex("by_external_id", "external_id")
Use indexes for queries and LiveGrid surfaces that filter, sort, or page through large tables.
Multi-Tenant Schemas
How tenant scoping appears in application tables depends on the project’s tenant database strategy.
Database per tenant
Application tables usually do not need tenant_id because each tenant has its own database.
Schema per tenant
Application tables usually do not need tenant_id because each tenant has its own Postgres schema.
Shared tables
Application tables need tenant_id and all queries must be scoped by tenant.
The runtime resolves and verifies the active tenant before executing functions.
Application code should read ctx.TenantID and ctx.DB instead of trusting a
tenant ID sent in function arguments.
Schema Sync
During development:
npx gonvex dev
Gonvex reads gonvex/schema.go, compares it to the project or tenant database,
and applies safe changes. Multi-tenant schema sync runs in parallel with bounded
concurrency and skips unchanged persisted schemas.
Safe automatic changes include:
- create tables
- add columns
- add indexes
- add unique indexes when existing data satisfies the constraint
- make a nullable column required only when no existing rows contain null
- relax a required column to nullable
Unsafe changes are rejected and shown in the CLI console:
- rename columns
- change column types
- drop columns
- drop tables
- rewrite large tables
- backfill production data
The rule is Convex-like: apply changes that preserve existing data automatically, reject changes that could lose or invalidate existing data.
Generated Schema Types
The CLI generates scoped schema metadata:
import { landlord, tenant } from "./gonvex/_generated/schema";
tenant.tables.tasks;
landlord.tables.projects;
Generated schema modules currently expose table/column/index metadata and table name types. Full document-row type inference from Go structs is not implemented.
Production Direction
Production migration tooling still needs:
- preview migrations before applying
- track migration versions
- support rollbacks where possible
- apply schema changes per tenant database route
- prevent destructive changes without confirmation
- expose schema status in the dashboard