Database Migrations in TypeScript: A Complete Overview of Tools and Practical Guide
Database migrations are a way to version and sequentially apply changes to a database schema (creating, modifying, or deleting tables, indexes, and constraints). In the TypeScript ecosystem, there are several powerful tools for managing migrations. In this article, we will take a detailed look at three of the most popular solutions: TypeORM, Prisma, and Drizzle ORM.
Why are migrations needed?
Without migrations, developers often make changes manually via an SQL console or admin panel, which leads to:
- Loss of synchronization between the development environment and production;
- Errors due to human factors;
- Difficulties when rolling back changes.
Migrations solve these problems by allowing you to automatically apply and roll back changes, as well as store the history of all schema changes in code.
1. TypeORM — The Classic Approach with ActiveRecord and Data Mapper
TypeORM is one of the most mature ORMs for TypeScript and JavaScript. It supports both the ActiveRecord and Data Mapper patterns and has a built-in migration system.
Installation and Setup
npm install typeorm reflect-metadata @types/nodenpm install pg # for PostgreSQLCreate a configuration file ormconfig.json or use data-source.ts:
import { DataSource } from "typeorm";
export const AppDataSource = new DataSource({ type: "postgres", host: "localhost", port: 5432, username: "user", password: "password", database: "mydb", entities: ["src/entity/*.ts"], migrations: ["src/migration/*.ts"], synchronize: false, // disable auto-synchronization});Creating and Running a Migration
First, generate a migration based on changes in entities:
npx typeorm migration:generate src/migration/AddUserTable -d src/data-source.tsExample of a generated migration:
import { MigrationInterface, QueryRunner, Table } from "typeorm";
export class AddUserTable1234567890 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.createTable( new Table({ name: "user", columns: [ { name: "id", type: "int", isPrimary: true, isGenerated: true, generationStrategy: "increment" }, { name: "name", type: "varchar", length: "255" }, { name: "email", type: "varchar", isUnique: true }, ], }) ); }
public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.dropTable("user"); }}Apply the migration:
npx typeorm migration:run -d src/data-source.tsPros and Cons of TypeORM
- Pros: large ecosystem, many databases (PostgreSQL, MySQL, SQLite, MSSQL), migration generation from entities.
- Cons: complex configuration, slow performance with a large number of entities, outdated codebase.
2. Prisma — Modern ORM with Automatic Migrations
Prisma is the new standard in TypeScript ORMs. It uses a declarative schema (schema.prisma) and automatically generates migrations.
Installation and Initialization
npm install prisma @prisma/clientnpx prisma initDefine the model in prisma/schema.prisma:
generator client { provider = "prisma-client-js"}
datasource db { provider = "postgresql" url = env("DATABASE_URL")}
model User { id