UNPKG

prisma-migrations

Version:

A Node.js library to manage Prisma ORM migrations like other ORMs

740 lines (520 loc) 17.1 kB
# Prisma Migrations Assists [Prisma](https://www.prisma.io/docs/orm/prisma-migrate) with migration tooling similar to other js ORMS, like [Knex](https://knexjs.org/guide/migrations.html#rollback) **Prisma Migrations** is a Node.js library and CLI tool that provides a Knex-like migration management approach for Prisma ORM. Write migrations with familiar `up` and `down` functions while leveraging Prisma's powerful client and type safety. ## Why Use This? - **Familiar Knex-like API** - `up` and `down` functions you already know - **TypeScript First** - Full type safety for your migrations - **Prisma Powered** - Use both raw SQL and Prisma operations - **Flexible Control** - Run specific migrations, rollback, dry-run - **Modern Build** - ESM/CJS dual support, Node.js 20+ ready ## Why Not Just Use Prisma? Prisma's native migration system is excellent for schema-driven development, but it lacks: - **No rollback functionality** - Once applied, migrations can't be easily undone - **Limited programmatic control** - Can't run specific numbers of migrations or rollback steps - **No up/down functions** - Migrations are pure SQL, no TypeScript/JavaScript logic - **Schema-only approach** - Difficult to mix schema changes with data seeding/transformation - **No granular migration management** - Can't easily target specific migrations or preview changes This library complements Prisma by providing the migration management patterns developers expect from other ORMs like Knex, while still leveraging Prisma's powerful client and type safety. ## Installation ```bash npm install prisma-migrations ``` ## Compatibility This library is designed to work with modern versions of Prisma and Node.js. Please ensure your environment meets these requirements: ### Minimum Requirements - **Prisma:** 2.0.0 or higher - **Node.js:** 20.0.0 or higher ### Supported Versions - **Prisma Client:** 4.0.0+ (peer dependency) - **Prisma CLI:** 2.0.0+ (peer dependency) - **Node.js:** 20.x, 21.x, 22.x, 23.x, 24.x ### Build Formats - **CommonJS CLI:** Works with Prisma 2.0.0+ - **ESM CLI:** Works with Prisma 3.15.0+ (requires ES module support) - **Library API:** Both ESM and CommonJS builds available ### Notes - CommonJS CLI (`dist/cli.cjs`) is used by default for maximum compatibility - ESM CLI (`dist/cli.js`) available for users with compatible Prisma versions - TypeScript migrations require `tsx` to be installed - Development and testing: Node.js 24+ is required for unit tests and mocking ## Quick Start ### New Classes and Interfaces #### CommitManager - Manages git-related actions. - **Methods**: - `getCurrentCommit()`: Retrieve current commit. - `getCurrentBranch()`: Retrieve current branch. - `getCommitsBetween(from, to)`: Get commits between references. #### VersionManager - Manages version-based migrations. - **Methods**: - `registerVersion(version, migrations)`: Register a version. - `getMigrationsBetween(from, to)`: Determine migrations to run or rollback. - `generateDeploymentPlan(fromVersion, toVersion)`: Generate a plan between versions. ### Interfaces - **VersionMigrationMapping**: Information associated with migrations for a version. - **VersionMigrationOptions**: Options for managing versions. - **VersionMigrationResult**: Result structure for version operations. ### CLI Usage ```bash # Create a new migration prisma-migrations create add_users_table # Run all pending migrations prisma-migrations up # Run migrations with options prisma-migrations up --steps 3 prisma-migrations up --dry-run # Rollback migrations prisma-migrations down prisma-migrations down --steps 2 # Check migration status prisma-migrations status ``` ### Programmatic Usage ```javascript import { MigrationManager } from "prisma-migrations"; const manager = new MigrationManager(); // Create a new migration await manager.createMigration({ name: "add_users_table" }); // Run all pending migrations await manager.runMigrations(); // Rollback last migration await manager.rollbackMigrations(); // Get migration status const status = await manager.getMigrationStatus(); ``` ## Migration Files Write migrations with familiar `up` and `down` functions, just like Knex: ```typescript import { PrismaClient } from "@prisma/client"; export async function up(prisma: PrismaClient): Promise<void> { // Raw SQL for schema changes await prisma.$executeRaw` CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; // Use Prisma operations for data seeding await prisma.user.createMany({ data: [ { email: "admin@example.com", name: "Admin User" }, { email: "user@example.com", name: "Test User" }, ], }); } export async function down(prisma: PrismaClient): Promise<void> { await prisma.$executeRaw`DROP TABLE IF EXISTS users`; } ``` **JavaScript migrations work too:** ```javascript exports.up = async function (prisma) { await prisma.$executeRaw` CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, user_id INTEGER REFERENCES users(id) ) `; }; exports.down = async function (prisma) { await prisma.$executeRaw`DROP TABLE IF EXISTS posts`; }; ``` ## Configuration Prisma Migrations can be configured in several ways: ### 1. package.json ```json { "prismaMigrations": { "migrationsDir": "./migrations", "schemaPath": "./prisma/schema.prisma", "tableName": "_prisma_migrations", "createTable": true, "migrationFormat": "ts", "extension": ".ts" } } ``` ### 2. Configuration file (prisma-migrations.config.js) ```javascript module.exports = { migrationsDir: "./migrations", schemaPath: "./prisma/schema.prisma", tableName: "_prisma_migrations", createTable: true, migrationFormat: "ts", // 'sql', 'js', or 'ts' extension: ".ts", // or '.js', '.sql' }; ``` ### Configuration Options | Option | Type | Default | Description | | ----------------- | ------------------- | -------------------------- | ------------------------------------------- | | `migrationsDir` | `string` | `'./migrations'` | Directory where migration files are stored | | `schemaPath` | `string` | `'./prisma/schema.prisma'` | Path to Prisma schema file | | `tableName` | `string` | `'_prisma_migrations'` | Name of the migrations tracking table | | `createTable` | `boolean` | `true` | Whether to auto-create the migrations table | | `migrationFormat` | `'sql'\|'js'\|'ts'` | `'ts'` | Format for new migration files | | `extension` | `string` | `'.ts'` | File extension for new migrations | ### 3. Environment Variables Set `DATABASE_URL` environment variable for database connection. --- ## API ### CLI Commands #### `prisma-migrations create <name>` Create a new migration file. **Parameters:** - `<name>`: Migration name (string) **Example:** ```bash prisma-migrations create add_users_table prisma-migrations create "update user schema" ``` --- #### `prisma-migrations up [options]` Run all pending migrations or specific migrations. **Options:** - `-t, --to <timestamp>`: Run up to a specific migration - `-s, --steps <number>`: Run a specific number of migrations - `-d, --dry-run`: Preview migrations without applying **Examples:** ```bash # Run all pending migrations prisma-migrations up # Run up to a specific migration prisma-migrations up --to 20231201120000 # Run next 3 migrations prisma-migrations up --steps 3 # Preview without applying prisma-migrations up --dry-run ``` --- #### `prisma-migrations down [options]` Rollback migrations. **Options:** - `-t, --to <timestamp>`: Rollback to a specific migration - `-s, --steps <number>`: Rollback a specific number of migrations - `-d, --dry-run`: Preview rollback without applying **Examples:** ```bash # Rollback last migration prisma-migrations down # Rollback to a specific migration prisma-migrations down --to 20231201120000 # Rollback last 2 migrations prisma-migrations down --steps 2 # Preview rollback prisma-migrations down --dry-run ``` --- #### `prisma-migrations status` Get the status of all migrations. **Example:** ```bash prisma-migrations status ``` **Output:** ``` add_users_table [applied] - 2023-12-01T12:00:00.000Z add_posts_table [pending] - Pending update_users_schema [applied] - 2023-12-01T13:00:00.000Z ``` --- #### `prisma-migrations test` Test database connection. **Example:** ```bash prisma-migrations test ``` --- ### Programmatic API #### `MigrationManager(configPath?)` Main class for managing migrations programmatically. **Parameters:** - `configPath?`: Optional path to configuration file (string) **Example:** ```javascript import { MigrationManager } from "prisma-migrations"; const manager = new MigrationManager("./custom-config.js"); ``` --- #### `manager.createMigration(options)` Create a new migration file. **Parameters:** - `options`: CreateMigrationOptions object - `name`: Migration name (string) - `directory?`: Optional custom directory (string) - `template?`: Optional custom template (MigrationTemplate) **Returns:** `Promise<MigrationFile>` **Example:** ```javascript const migration = await manager.createMigration({ name: "add_users_table", template: { up: "CREATE TABLE users (id SERIAL PRIMARY KEY);", down: "DROP TABLE users;", }, }); ``` --- #### `manager.runMigrations(options?)` Run pending migrations. **Parameters:** - `options?`: RunMigrationOptions object - `to?`: Run up to specific migration (string) - `steps?`: Number of migrations to run (number) - `dryRun?`: Preview without applying (boolean) - `force?`: Force run even if already applied (boolean) **Returns:** `Promise<MigrationResult>` **Example:** ```javascript // Run all pending migrations const result = await manager.runMigrations(); // Run with options const result = await manager.runMigrations({ steps: 3, dryRun: true, }); ``` --- #### `manager.rollbackMigrations(options?)` Rollback applied migrations. **Parameters:** - `options?`: RollbackMigrationOptions object - `to?`: Rollback to specific migration (string) - `steps?`: Number of migrations to rollback (number) - `dryRun?`: Preview without applying (boolean) - `force?`: Force rollback even without down script (boolean) **Returns:** `Promise<MigrationResult>` **Example:** ```javascript // Rollback last migration const result = await manager.rollbackMigrations(); // Rollback with options const result = await manager.rollbackMigrations({ steps: 2, dryRun: true, }); ``` --- #### `manager.getMigrationStatus()` Get status of all migrations. **Returns:** `Promise<MigrationStatus[]>` **Example:** ```javascript const statuses = await manager.getMigrationStatus(); statuses.forEach((status) => { console.log(`${status.name} [${status.status}]`); }); ``` --- #### `manager.getMigrationState()` Get detailed migration state information. **Returns:** `Promise<MigrationState>` **Example:** ```javascript const state = await manager.getMigrationState(); console.log("Applied:", state.applied.length); console.log("Pending:", state.pending.length); ``` --- #### `manager.testConnection()` Test database connection. **Returns:** `Promise<boolean>` **Example:** ```javascript const isConnected = await manager.testConnection(); if (isConnected) { console.log("Database connection successful"); } ``` --- ### Configuration Classes #### `ConfigManager(configPath?)` Manages configuration loading and access. **Methods:** - `getConfig()`: Get current configuration - `updateConfig(updates)`: Update configuration - `getDatabaseUrl()`: Get database URL from various sources --- #### `FileManager(migrationsDir)` Manages migration file operations. **Methods:** - `createMigrationFile(name, template?)`: Create new migration file - `readMigrationFiles()`: Read all migration files - `getMigrationFile(timestamp)`: Get specific migration file - `getMigrationByName(name)`: Find migration by name - `parseMigrationContent(content)`: Parse UP/DOWN sections --- #### `DatabaseAdapter(databaseUrl, tableName?)` Handles database operations and migration tracking. **Methods:** - `connect()`: Connect to database - `disconnect()`: Disconnect from database - `getAppliedMigrations()`: Get all applied migrations - `recordMigration(id, name)`: Record migration as applied - `removeMigration(id)`: Remove migration record - `executeMigration(sql)`: Execute migration SQL --- ### Type Definitions #### `MigrationConfig` ```typescript interface MigrationConfig { migrationsDir: string; schemaPath: string; databaseUrl?: string; tableName?: string; createTable?: boolean; } ``` #### `Migration` ```typescript interface Migration { id: string; name: string; filename: string; timestamp: Date; applied: boolean; appliedAt?: Date; rollback?: string; } ``` #### `MigrationResult` ```typescript interface MigrationResult { success: boolean; migrations: Migration[]; error?: string; } ``` #### `MigrationStatus` ```typescript interface MigrationStatus { id: string; name: string; status: "pending" | "applied" | "error"; appliedAt?: Date; error?: string; } ``` --- ### Migration File Formats Prisma Migrations supports both SQL and JavaScript/TypeScript migration files: #### SQL Format (Traditional) ```sql -- UP CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- DOWN DROP TABLE users; ``` #### JavaScript/TypeScript Format (Knex-like) **TypeScript (.ts files):** ```typescript import { PrismaClient } from "@prisma/client"; export async function up(prisma: PrismaClient): Promise<void> { // Raw SQL approach await prisma.$executeRaw` CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; // Or use Prisma operations for data seeding await prisma.user.createMany({ data: [{ email: "admin@example.com" }, { email: "user@example.com" }], }); } export async function down(prisma: PrismaClient): Promise<void> { await prisma.$executeRaw`DROP TABLE IF EXISTS users`; } ``` **JavaScript (.js files):** ```javascript exports.up = async function (prisma) { await prisma.$executeRaw` CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; }; exports.down = async function (prisma) { await prisma.$executeRaw`DROP TABLE IF EXISTS users`; }; ``` #### Configuration Set the migration format in your configuration: ```javascript // prisma-migrations.config.js module.exports = { migrationFormat: "ts", // 'sql', 'js', or 'ts' migrationsDir: "./migrations", // ... other options }; ``` **Note:** TypeScript migrations require `tsx` to be installed: ```bash npm install tsx ``` --- ## Version-Based Migration Management Manage migrations using git commits or semantic versioning for deployment and rollback scenarios. ### Git Commit-Based Migrations ```javascript // Deploy to specific commit const manager = new MigrationManager(); const commitSha = "abc123"; const migrations = await manager.getMigrationsByCommit(commitSha); await manager.runMigrations({ to: migrations[migrations.length - 1].id }); // Rollback to previous commit const previousCommit = "def456"; const targetMigrations = await manager.getMigrationsByCommit(previousCommit); await manager.rollbackMigrations({ to: targetMigrations[targetMigrations.length - 1].id, }); ``` ### Semantic Versioning ```javascript // Register version with migrations const manager = new MigrationManager(); await manager.registerVersion("1.2.0", ["20231201120000", "20231201130000"]); // Deploy to version await manager.deployToVersion("1.2.0"); // Rollback to previous version await manager.rollbackToVersion("1.1.0"); ``` ### CLI Usage ```bash # Deploy to git commit prisma-migrations up --commit abc123 # Rollback to commit prisma-migrations down --commit def456 # Deploy to version prisma-migrations up --version 1.2.0 # Rollback to version prisma-migrations down --version 1.1.0 ``` --- ## Development Use [Corepack](https://nodejs.org/api/corepack.html) to manage Yarn and ensure you have the latest Node and npm. ```bash corepack enable npm install ``` ### Key Tasks - `npm run build` - Build the TypeScript source - `npm test` - Run unit tests - `npm run test:docker` - Run end-to-end tests in Docker - `npm run lint` - Run oxlint on source code - `npm run format` - Format code with prettier Use `npm run` commands for task execution.