Migrating from v0.3.x to v0.4.0

Comprehensive guide for upgrading from v0.3.x to v0.4.0, including breaking changes, new features, and migration steps.

Table of contents

  1. Overview
  2. Breaking Changes Summary
  3. Breaking Change #1: filePattern → filePatterns
    1. What Changed
    2. Migration Steps
    3. Default Behavior
    4. Migration Examples
  4. Breaking Change #2: MigrationScriptExecutor API Methods
    1. What Changed
    2. Migration Steps
      1. Change 1: migrate() and migrateTo()
      2. Change 2: downTo() → down()
    3. Full Example
  5. Breaking Change #3: IDB.checkConnection() Required
    1. What Changed
    2. Migration Steps
    3. When It’s Called
  6. Breaking Change #4: ISchemaVersion Property Rename
    1. What Changed
    2. Migration Steps
  7. Breaking Change #5: IMigrationScript Method Rename
    1. What Changed
    2. Migration Steps
  8. Breaking Change #6: Service Method Renames
    1. What Changed
    2. Migration Steps
  9. New Features
    1. 1. SQL Migration Files
    2. 2. Dry Run Mode
    3. 3. Execution Summary Logging
    4. 4. Loader Architecture
  10. Migration Checklist
    1. Step 1: Update Configuration
    2. Step 2: Update API Calls
    3. Step 3: Update Database Handler
    4. Step 4: Update Custom Implementations
    5. Step 5: Test Migration
    6. Step 6: Update Documentation
  11. Troubleshooting
    1. Error: “filePattern is not defined”
    2. Error: “migrateTo is not a function”
    3. Error: “checkConnection is not a function”
    4. Error: “migrations property not found”
  12. Getting Help

Overview

Version 0.4.0 introduces significant new features and API improvements:

  • SQL migration files support (.up.sql / .down.sql)
  • 🔄 Simplified API with clearer method names
  • 🔍 Dry run mode for safe migration preview
  • 📊 Execution summary logging with file rotation
  • Connection validation with checkConnection()
  • 🛡️ Improved type safety with loader architecture

Compatibility: v0.4.0 includes breaking changes that require code updates. Follow this guide to migrate smoothly.


Breaking Changes Summary

Change Impact Migration Effort
filePatternfilePatterns High Low - Simple property rename
API method renames High Medium - Update all call sites
IDB.checkConnection() required High Low - Add one method
ISchemaVersion.migrationsmigrationRecords Medium Low - Property rename
IMigrationScript.getAll()getAllExecuted() Low Low - Method rename
Service method renames Low Low - Internal API changes

Breaking Change #1: filePattern → filePatterns

What Changed

The filePattern property has been removed and replaced with filePatterns (array) to support multiple file types.

Reason: v0.4.0 adds SQL migration support, requiring matching multiple file patterns (.ts, .js, .up.sql).

Migration Steps

Before (v0.3.x):

const config = new Config();
config.filePattern = /^V(\d{12})_.*\.ts$/;

After (v0.4.0):

const config = new Config();
config.filePatterns = [
  /^V(\d{12})_.*\.ts$/
];

Default Behavior

If you relied on the default filePattern, v0.4.0’s default filePatterns includes TypeScript, JavaScript, and SQL:

// Default in v0.4.0
config.filePatterns = [
  /^V(\d{12})_.*\.ts$/,
  /^V(\d{12})_.*\.js$/,
  /^V(\d{12})_.*\.up\.sql$/
];

Migration Examples

TypeScript only:

// v0.3.x
config.filePattern = /^V(\d{12})_.*\.ts$/;

// v0.4.0
config.filePatterns = [/^V(\d{12})_.*\.ts$/];

TypeScript and JavaScript:

// v0.3.x (not possible without custom logic)
config.filePattern = /^V(\d{12})_.*\.(ts|js)$/;

// v0.4.0 (cleaner)
config.filePatterns = [
  /^V(\d{12})_.*\.ts$/,
  /^V(\d{12})_.*\.js$/
];

Custom prefix:

// v0.3.x
config.filePattern = /^MIG_(\d+)_(.+)\.ts$/;

// v0.4.0
config.filePatterns = [/^MIG_(\d+)_(.+)\.ts$/];

Breaking Change #2: MigrationScriptExecutor API Methods

What Changed

Method names have been simplified and clarified:

v0.3.x Method v0.4.0 Method Notes
migrate() up() or migrate() Both work, migrate() is alias
migrateTo(version) Removed Use up(version) instead
downTo(version) down(version) Clearer naming

Reason: Consolidates migration methods and aligns with common migration tool conventions (up / down).

Migration Steps

Change 1: migrate() and migrateTo()

Before (v0.3.x):

// Run all pending migrations
await executor.migrate();

// Migrate to specific version
await executor.migrateTo(202501280300);

After (v0.4.0):

// Run all pending migrations (two options)
await executor.up();        // New preferred method
await executor.migrate();   // Still works (alias to up())

// Migrate to specific version
await executor.up(202501280300);  // migrateTo() removed

Change 2: downTo() → down()

Before (v0.3.x):

await executor.downTo(202501280100);

After (v0.4.0):

await executor.down(202501280100);

Full Example

Before (v0.3.x):

import { MigrationScriptExecutor, Config } from '@migration-script-runner/core';

const executor = new MigrationScriptExecutor(handler, config);

// Run all migrations
const result = await executor.migrate();

// Migrate to version 5
await executor.migrateTo(202501280005);

// Rollback to version 2
await executor.downTo(202501280002);

After (v0.4.0):

import { MigrationScriptExecutor, Config } from '@migration-script-runner/core';

const executor = new MigrationScriptExecutor(handler, config);

// Run all migrations (choose one)
const result = await executor.up();       // Preferred
const result = await executor.migrate();  // Also works

// Migrate to version 5
await executor.up(202501280005);

// Rollback to version 2
await executor.down(202501280002);

Breaking Change #3: IDB.checkConnection() Required

What Changed

The IDB interface now requires a checkConnection() method. This method is called before migration operations to fail fast if the database connection is invalid.

interface IDB {
  checkConnection(): Promise<void>;
  [key: string]: unknown;
}

Reason: Provides early detection of connection issues, giving clearer error messages before attempting migrations.

Migration Steps

Add checkConnection() to your database implementation:

PostgreSQL Example:

import { Pool } from 'pg';
import { IDB } from '@migration-script-runner/core';

class PostgresDB implements IDB {
  constructor(private pool: Pool) {}

  async checkConnection(): Promise<void> {
    try {
      await this.pool.query('SELECT 1');
    } catch (error) {
      throw new Error(`PostgreSQL connection failed: ${error.message}`);
    }
  }

  async query(sql: string): Promise<unknown> {
    const result = await this.pool.query(sql);
    return result.rows;
  }
}

MongoDB Example:

import { MongoClient } from 'mongodb';
import { IDB } from '@migration-script-runner/core';

class MongoDB implements IDB {
  constructor(private client: MongoClient) {}

  async checkConnection(): Promise<void> {
    try {
      await this.client.db('admin').command({ ping: 1 });
    } catch (error) {
      throw new Error(`MongoDB connection failed: ${error.message}`);
    }
  }

  getCollection(name: string) {
    return this.client.db().collection(name);
  }
}

MySQL Example:

import { Connection } from 'mysql2/promise';
import { IDB } from '@migration-script-runner/core';

class MySQLDB implements IDB {
  constructor(private connection: Connection) {}

  async checkConnection(): Promise<void> {
    try {
      await this.connection.ping();
    } catch (error) {
      throw new Error(`MySQL connection failed: ${error.message}`);
    }
  }

  async query(sql: string): Promise<unknown> {
    const [rows] = await this.connection.execute(sql);
    return rows;
  }
}

When It’s Called

checkConnection() is called automatically before:

  • executor.up()
  • executor.down()
  • executor.validate()

Breaking Change #4: ISchemaVersion Property Rename

What Changed

The migrations property has been renamed to migrationRecords for clarity.

// v0.3.x
interface ISchemaVersion {
  migrations: IMigrationScript;
}

// v0.4.0
interface ISchemaVersion {
  migrationRecords: IMigrationScript;
}

Reason: “migrationRecords” is more descriptive and distinguishes between migration files and execution records.

Migration Steps

Before (v0.3.x):

class MySchemaVersion implements ISchemaVersion {
  migrations: IMigrationScript;

  constructor() {
    this.migrations = new MigrationScriptDAO();
  }
}

After (v0.4.0):

class MySchemaVersion implements ISchemaVersion {
  migrationRecords: IMigrationScript;

  constructor() {
    this.migrationRecords = new MigrationScriptDAO();
  }
}

Impact: Only affects custom ISchemaVersion implementations. Most users use MSR’s built-in SchemaVersionService.


Breaking Change #5: IMigrationScript Method Rename

What Changed

// v0.3.x
interface IMigrationScript {
  getAll(): Promise<IMigrationInfo[]>;
}

// v0.4.0
interface IMigrationScript {
  getAllExecuted(): Promise<IMigrationInfo[]>;
}

Reason: Method name now explicitly describes what it returns (executed migrations only, not all migration files).

Migration Steps

Before (v0.3.x):

class MigrationScriptDAO implements IMigrationScript {
  async getAll(): Promise<IMigrationInfo[]> {
    return await this.db.query('SELECT * FROM schema_version');
  }
}

After (v0.4.0):

class MigrationScriptDAO implements IMigrationScript {
  async getAllExecuted(): Promise<IMigrationInfo[]> {
    return await this.db.query('SELECT * FROM schema_version');
  }
}

Impact: Only affects custom IMigrationScript implementations.


Breaking Change #6: Service Method Renames

What Changed

Internal service methods have been renamed for consistency:

Interface v0.3.x Method v0.4.0 Method
IMigrationService readMigrationScripts() findMigrationScripts()
IMigrationService getBeforeMigrateScript() findBeforeMigrateScript()
IBackup restore(data) restore(backupData)

Reason: “find” is more accurate for discovery operations; “backupData” parameter is clearer.

Migration Steps

Only needed if you’re using these services directly or implementing custom services.

Before (v0.3.x):

const migrationService = new MigrationService();
const scripts = await migrationService.readMigrationScripts(config);
const beforeMigrate = await migrationService.getBeforeMigrateScript(config);

After (v0.4.0):

const migrationService = new MigrationService();
const scripts = await migrationService.findMigrationScripts(config);
const beforeMigrate = await migrationService.findBeforeMigrateScript(config);

Impact: Low - These are internal APIs rarely used directly.


New Features

1. SQL Migration Files

v0.4.0 adds first-class support for SQL migration files alongside TypeScript migrations.

Quick Start:

-- migrations/V202501280100_create_users.up.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE
);
-- migrations/V202501280100_create_users.down.sql
DROP TABLE IF EXISTS users;

ISqlDB Interface:

For SQL migrations, implement ISqlDB:

import { ISqlDB } from '@migration-script-runner/core';

class PostgresDB implements ISqlDB {
  async query(sql: string): Promise<unknown> {
    const result = await this.pool.query(sql);
    return result.rows;
  }

  async checkConnection(): Promise<void> {
    await this.pool.query('SELECT 1');
  }
}

Learn More: SQL Migrations Guide


2. Dry Run Mode

Preview migrations without executing them:

const config = new Config();
config.dryRun = true;

const result = await executor.up();
// Shows what would be executed, but doesn't run anything

Use cases:

  • CI/CD validation
  • Production safety checks
  • Migration planning

Learn More: Configuration - dryRun


3. Execution Summary Logging

Automatic logging of migration execution to JSON/text files:

const config = new Config();
config.logging.enabled = true;
config.logging.folder = './logs';
config.logging.format = 'JSON';
config.logging.maxFiles = 10;

await executor.up();
// Creates: logs/migration-summary-2025-01-28T10-30-00.json

Log contents:

  • All executed migrations
  • Backup operations
  • Rollback events
  • Errors and timing

Learn More: Configuration - Logging Settings


4. Loader Architecture

Extensible system for supporting multiple file types:

import {
  LoaderRegistry,
  TypeScriptLoader,
  SqlLoader
} from '@migration-script-runner/core';

// Default loaders
const registry = LoaderRegistry.createDefault();
// Includes: TypeScriptLoader, SqlLoader

// Custom loader
class PythonLoader implements IMigrationScriptLoader {
  canHandle(filePath: string): boolean {
    return filePath.endsWith('.py');
  }

  async load(script: MigrationScript): Promise<IRunnableScript> {
    // Load and execute Python migration
  }

  getName(): string {
    return 'PythonLoader';
  }
}

registry.register(new PythonLoader());

Learn More: API Reference - Loaders


Migration Checklist

Use this checklist to ensure a complete migration:

Step 1: Update Configuration

  • Change config.filePattern to config.filePatterns (array)
  • Update any custom file pattern logic
  • Review default patterns if you relied on defaults

Step 2: Update API Calls

  • Replace migrateTo(version) with up(version)
  • Replace downTo(version) with down(version)
  • Update migrate() to up() (optional, migrate() still works)

Step 3: Update Database Handler

  • Add checkConnection() method to IDB implementation
  • Test connection validation
  • (Optional) Implement ISqlDB for SQL migrations

Step 4: Update Custom Implementations

  • Rename migrationsmigrationRecords in ISchemaVersion implementations
  • Rename getAll()getAllExecuted() in IMigrationScript implementations
  • Update service method calls if using internal APIs

Step 5: Test Migration

  • Run migrations in development
  • Test rollback functionality
  • Verify all migrations execute correctly
  • Test dry run mode
  • Validate connection checking

Step 6: Update Documentation

  • Update deployment scripts
  • Update team documentation
  • Update CI/CD pipelines

Troubleshooting

Error: “filePattern is not defined”

Problem: You’re still using config.filePattern in v0.4.0

Solution:

// Change this:
config.filePattern = /^V(\d{12})_.*\.ts$/;

// To this:
config.filePatterns = [/^V(\d{12})_.*\.ts$/];

Error: “migrateTo is not a function”

Problem: migrateTo() was removed in v0.4.0

Solution:

// Change this:
await executor.migrateTo(202501280005);

// To this:
await executor.up(202501280005);

Error: “checkConnection is not a function”

Problem: Your IDB implementation doesn’t have checkConnection()

Solution:

class MyDB implements IDB {
  // Add this method:
  async checkConnection(): Promise<void> {
    // Verify your connection works
    await this.yourDatabase.ping();
  }
}

Error: “migrations property not found”

Problem: ISchemaVersion property was renamed

Solution:

// Change this:
class MySchemaVersion implements ISchemaVersion {
  migrations: IMigrationScript;
}

// To this:
class MySchemaVersion implements ISchemaVersion {
  migrationRecords: IMigrationScript;
}

Getting Help

If you encounter issues during migration:

  1. Check this guide - Most common issues are covered above
  2. Review the changelog - GitHub Releases
  3. Search issues - GitHub Issues
  4. Ask for help - GitHub Discussions
  5. Report bugs - Create an issue