TypeORM PostgreSQL Migration Setup: Production-Ready Configuration
Quick Answer
- Conclusion: TypeORM migrations provide a reliable, version-controlled way to manage PostgreSQL schema changes in production, replacing the unsafe
synchronize: trueapproach. - First checks: Ensure
synchronize: falsein your data source config, install thepgdriver withnpm install pg, and define amigrationsarray pointing to your migration files. - Minimal command: Run
npx typeorm migration:run -d ./path/to/data-source.tsafter configuring your DataSource withhost,username,password,database, andmigrationspaths. - Version boundary: This setup applies to TypeORM 0.3.x+ using the DataSource API; the legacy
ormconfig.jsonapproach is deprecated.
What Problem It Solves
TypeORM's synchronize: true automatically syncs your entity definitions to the database schema on every application launch. While convenient during prototyping, this is dangerous in production—it can drop columns, alter tables, or delete data without warning. Migrations solve this by providing explicit, versioned, and reversible schema change files that you review, test, and deploy deliberately.
Installation and Quick Start
Install the PostgreSQL driver:
BASHnpm install pg
Create a DataSource configuration file (e.g., data-source.ts):
TYPESCRIPTimport { DataSource } from "typeorm"; export const AppDataSource = new DataSource({ type: "postgres", host: "localhost", port: 5432, username: "your_user", password: "your_password", database: "your_database", synchronize: false, migrations: ["src/migrations/*.ts"], migrationsTableName: "migrations", });
Generate your first migration:
BASHnpx typeorm migration:generate src/migrations/InitialSchema -d ./data-source.ts
Run pending migrations:
BASHnpx typeorm migration:run -d ./data-source.ts
Minimal Working Configuration
The following DataSource configuration is the minimum required for a production-safe migration setup:
TYPESCRIPTimport { DataSource } from "typeorm"; export const AppDataSource = new DataSource({ type: "postgres", host: process.env.DB_HOST || "localhost", port: parseInt(process.env.DB_PORT || "5432", 10), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: false, // CRITICAL: must be false for migrations migrations: ["dist/migrations/*.js"], // compiled migration files migrationsTableName: "migrations", // stores migration history });
Key points:
synchronize: falseis mandatory when using migrations.- The
migrationsarray accepts glob patterns; usedist/migrations/*.jsfor production andsrc/migrations/*.tsfor development with ts-node. migrationsTableNamedefaults to"migrations"; you can customize it if needed.
Parameters and Environment Variables
Essential Connection Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
host | Yes | — | Database host address |
port | No | 5432 | Database host port |
username | Yes | — | Database username |
password | Yes | — | Database password |
database | Yes | — | Database name |
url | No | — | Connection URL; other parameters override values from URL |
Migration-Specific Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
synchronize | No | false | Must be false when using migrations |
migrations | Yes | — | Array of migration classes or glob patterns |
migrationsRun | No | false | Auto-run migrations on app launch |
migrationsTableName | No | "migrations" | Table name for tracking executed migrations |
migrationsTransactionMode | No | "all" | Transaction mode: "all", "none", or "each" |
PostgreSQL-Specific Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
schema | No | "public" | Database schema name |
ssl | No | — | SSL/TLS configuration object |
uuidExtension | No | "uuid-ossp" | UUID generation extension ("uuid-ossp" or "pgcrypto") |
connectTimeoutMS | No | undefined | Connection timeout in milliseconds |
poolErrorHandler | No | warn log | Handler for pool error events |
maxTransactionRetries | No | 5 | Max retries for serialization failures (40001) |
logNotifications | No | false | Log Postgres server notices and notifications |
installExtensions | No | true | Auto-install required Postgres extensions |
extensions | No | undefined | Additional Postgres extensions to install |
applicationName | No | undefined | Application name visible in Postgres stats/logs |
parseInt8 | No | false | Parse int8 as JavaScript numbers (default: strings) |
Root Cause Analysis
The most common migration failures stem from three root causes:
-
Missing
synchronize: false: Ifsynchronizeistrue, TypeORM will attempt to sync the schema on every connection, potentially overwriting migration changes or causing conflicts. -
Incorrect migration path: The
migrationsarray must point to compiled.jsfiles in production. Using.tspaths withts-nodeworks in development but fails in production builds. -
Concurrent migration execution: Running migrations from multiple application instances simultaneously can cause race conditions, duplicate entries in the migrations table, or partial schema changes.
Common Errors and Fixes
| Error | Cause | Solution |
|---|---|---|
Error: Cannot connect to database. Connection refused. | Database not running or incorrect connection parameters | Verify host, port, username, password, database values; check firewall rules |
Error: relation "migrations" does not exist | Migrations table missing | TypeORM creates this table automatically on first run; ensure migrationsTableName matches your config |
Error: Migration "MigrationName" has already been run. | Duplicate migration execution | Check the migrations table; use typeorm migration:revert to roll back, or manually remove the record |
QueryFailedError: duplicate key value violates unique constraint | Migration tries to insert duplicate data or create existing index | Review migration SQL; clean conflicting data in development environments |
Production Notes and Security Checks
Deployment Safety
- Single-instance execution: Run migrations from only one instance (e.g., a Kubernetes Job or a deployment hook) to prevent concurrent conflicts.
- Transaction mode: Set
migrationsTransactionMode: "each"so each migration runs in its own transaction. If one fails, only that migration is rolled back. - Backup before migration: Always take a database backup before running migrations in production.
Security Hardening
- Dedicated migration user: Create a PostgreSQL user with only the DDL permissions needed (
CREATE TABLE,ALTER TABLE, etc.) and restrict access to other databases. - Credential management: Never hardcode credentials. Use environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault).
- SSL enforcement: In production, configure
ssl: { rejectUnauthorized: true }to enforce encrypted connections.
Performance Considerations
- Split large migrations: Break large schema changes (e.g., adding indexes to large tables) into separate migration files.
- Schedule during low traffic: Run migrations during maintenance windows or low-traffic periods.
- Monitor execution: Enable
logNotifications: trueto capture Postgres server notices during migration execution.
FAQ
Q: How do I safely run TypeORM migrations in production?
A: Follow these best practices:
- Backup the database before running migrations.
- Set
migrationsTransactionMode: "each"for per-migration transaction isolation. - Run migrations during low-traffic periods.
- Execute from a single instance (Kubernetes Job, deployment hook).
- Enable detailed logging to monitor execution.
- Have a rollback plan ready using
typeorm migration:revert.
Q: What are the advantages of TypeORM migrations over raw SQL migration files?
A: TypeORM migrations offer:
- Auto-generation:
typeorm migration:generatedetects entity-database differences and creates migration files automatically. - TypeScript support: Write migrations with full type checking and IDE support.
- ORM integration: Migrations stay in sync with entity definitions.
- Flexible transaction control: Choose between
"all","each", or"none"transaction modes. - Built-in rollback: The
revertcommand handles rollbacks without manual scripts. - Cross-database compatibility: Migration files work across supported databases (with database-specific syntax caveats).
Q: How do I handle migration conflicts when multiple developers generate migrations?
A: Best practices for conflict resolution:
- Timestamp naming: TypeORM uses timestamps in migration filenames, reducing naming collisions.
- Frequent sync: Pull latest code and run
typeorm migration:runlocally before generating new migrations. - Code review: Review migration files during pull requests to verify order and correctness.
- Branch strategy: Generate migrations in feature branches; resolve conflicts when merging to main.
- Manual ordering: If migration file order conflicts, adjust timestamps in filenames to ensure correct execution sequence.