This document describes the database migration system in Trigger.dev, focusing on how schema changes are managed, applied, and deployed across different environments. The system uses Prisma as the primary ORM and migration tool for the PostgreSQL database, with additional migration handling for the Graphile Worker job queue infrastructure.
For information about the overall database schema structure, see Database Schema Overview. For details on specific data models, see Task and Run Models, Organization and Project Models, and Deployment and Worker Models.
Trigger.dev uses Prisma as its Object-Relational Mapping (ORM) tool and database migration manager. Prisma provides type-safe database access and a declarative migration workflow.
The database schema is defined in schema.prisma files using Prisma's schema language. The schema includes:
The Prisma client is instantiated and used throughout the codebase for database operations, as seen in apps/webapp/app/services/worker.server.ts4 where both prisma and $replica are imported for write and read operations respectively.
Prisma migrations are generated using the Prisma CLI:
When a migration is generated, Prisma:
Migrations are applied differently depending on the environment:
Development Environment:
prisma migrate dev command applies pending migrationsProduction Environment:
prisma migrate deploy to apply pending migrationsSources: apps/webapp/app/services/worker.server.ts1-131
In addition to Prisma migrations for the main database schema, Trigger.dev uses Graphile Worker for background job processing, which maintains its own database schema and migration system.
The GraphileMigrationHelperService handles migrations specific to the Graphile Worker infrastructure. This service is invoked during worker initialization to ensure the Graphile Worker schema is up-to-date.
Initialization Flow:
The migration helper is called early in the application lifecycle:
apps/webapp/app/services/worker.server.ts124-131
async function init() {
const migrationHelper = new GraphileMigrationHelperService();
await migrationHelper.call();
if (env.WORKER_ENABLED === "true") {
await workerQueue.initialize();
}
}
This ensures that:
The Graphile Worker maintains several database objects in a separate schema:
These migrations are separate from Prisma migrations because Graphile Worker manages its own schema versioning and migration system.
Sources: apps/webapp/app/services/worker.server.ts124-131
schema.prisma with new models, fields, or relationshipsprisma migrate dev --name <description>prisma migrate deploy applies pending migrationsGraphileMigrationHelperService applies Graphile Worker migrationsSources: apps/webapp/app/services/worker.server.ts124-131
The codebase maintains multiple database client instances for different purposes:
Client Instantiation:
prisma: Main client for write operations and transactions$replica: Read-only client pointing to database replica for improved performanceThe ZodWorker in apps/webapp/app/services/worker.server.ts136-137 is configured with both clients:
prisma,
replica: $replica,
The worker queue uses connection pooling to manage database connections efficiently:
apps/webapp/app/services/worker.server.ts138-145
runnerOptions: {
connectionString: env.DATABASE_URL,
concurrency: env.WORKER_CONCURRENCY,
pollInterval: env.WORKER_POLL_INTERVAL,
noPreparedStatements: env.DATABASE_URL !== env.DIRECT_URL,
schema: env.WORKER_SCHEMA,
maxPoolSize: env.WORKER_CONCURRENCY + 1,
}
The maxPoolSize is set to WORKER_CONCURRENCY + 1 to ensure adequate connections for concurrent job processing.
Sources: apps/webapp/app/services/worker.server.ts134-145
Migrations should be designed with transactional safety in mind:
For production deployments, follow these patterns:
The migration system supports rolling deployments by ensuring:
When both Prisma and Graphile Worker schemas require updates:
public)graphile_worker schemaSources: apps/webapp/app/services/worker.server.ts124-131
Prisma maintains migration history in the _prisma_migrations table:
The schema version is determined by:
While Prisma supports migration rollback in development:
Production rollbacks are more complex and typically involve:
Sources: apps/webapp/app/services/worker.server.ts1-131
Database connection configuration varies by environment:
| Configuration | Development | Production |
|---|---|---|
| Primary URL | DATABASE_URL | DATABASE_URL |
| Direct Connection | DIRECT_URL | DIRECT_URL |
| Read Replica | Optional | DATABASE_REPLICA_URL |
| Migration Strategy | migrate dev | migrate deploy |
| Worker Schema | env.WORKER_SCHEMA | env.WORKER_SCHEMA |
| Connection Pooling | Lower limits | Higher limits |
The distinction between DATABASE_URL and DIRECT_URL is important:
DATABASE_URL: May point to a connection pooler (e.g., PgBouncer)DIRECT_URL: Direct connection for migrations and schema operationsThe noPreparedStatements flag is set when using connection poolers that don't support prepared statements:
apps/webapp/app/services/worker.server.ts142
noPreparedStatements: env.DATABASE_URL !== env.DIRECT_URL
Sources: apps/webapp/app/services/worker.server.ts138-145
Key locations:
apps/webapp/prisma/schema.prismaapps/webapp/prisma/migrations/apps/webapp/app/services/db/graphileMigrationHelper.server.tsapps/webapp/app/db.server.tsSources: apps/webapp/app/services/worker.server.ts29 apps/webapp/app/services/worker.server.ts4
Refresh this wiki
This wiki was recently refreshed. Please wait 7 days to refresh again.