Simple extensible node migrator library.
Simple extensible one-way migration tool for performing various tasks in order in multiple environments.
- Can be used for running any kind of tasks that need to be executed on multiple environments.
- Works great for migrating database schemas and data but also for changing image file formats etc.
- The storage method is extensive so the information about which migrations have been carried out can be stored anywhere (database, simple file etc).
- Has built-in TypeORM storage that can use MySQL / MariaDB / Postgres / SQLite / Microsoft SQL Server / Oracle / WebSQL.
- Each migration is a simple async function receiving a custom context and returning anything serializable for success or throwing error on failure.
- Written in TypeScript.
- Includes 100% test coverage.
Installation
This package is distributed via npm
npm install migrator-js
Commands
-
yarn start
to start the example application. -
yarn build
to build the production version. -
yarn test
to run tests. -
yarn coverage
to gather code coverage. -
yarn lint
to lint the codebase. -
yarn prettier
to run prettier. -
yarn audit
to run all pre-commit checks (prettier, build, lint, test)
Example migration script
import { IMigrationContext } from "../";
export default async (context: IMigrationContext): Promise<string> => {
// run any query, crop images etc
const sum = await context.connection.query("SELECT 1+1 AS sum");
return `1+1=${sum}`;
};
Example script that runs chosen migrations
Store it in src/scripts/migrate.ts
etc and add NPM script to run it.
package.json
{
"scripts": {
"migrate": "yarn build && node build/example"
}
}
src/scripts/migrate.ts
import chalk from "chalk";
import path from "path";
import { Migrator, MigratorTypeormStorage } from "../src";
// the contents of this file is usually kept in scripts/migrate.ts etc file and run through NPM scripts
// any context resources passed on to all migrations
export interface MigrationContext {
version: string;
}
async function run() {
// show an empty line between previous content
console.log("");
// create migrator
const migrator = new Migrator<MigrationContext>(
// this is the custom context matching MigrationContext
{
version: "1",
},
// migrator configuration
{
// pattern for finding the migration scripts
pattern: path.join(__dirname, "migrations", "!(*.spec|*.test|*.d).{ts,js}"),
// you can use MySQL / MariaDB / Postgres / SQLite / Microsoft SQL Server / Oracle / WebSQL
// see http://typeorm.io
storage: new MigratorTypeormStorage({
type: "sqlite",
database: path.join(__dirname, "..", "..", "migrate.sqlite3"),
}),
},
);
// attempt to run the migrator
try {
// run migrator providing pattern of migration files, storage to use and context to pass to each migration
// run the migrations and extract results
const { pendingMigrations, chosenMigrations, performedMigrations, failedMigrations } = await migrator.migrate();
// print results to console
if (pendingMigrations.length === 0) {
console.error(`${chalk.black.bgWhite(` NOTHING TO MIGRATE `)} `);
} else if (chosenMigrations.length === 0) {
console.error(`${chalk.black.bgWhite(` NO MIGRATIONS CHOSEN `)} `);
} else if (performedMigrations.length > 0 && failedMigrations.length === 0) {
console.error(`${chalk.black.bgGreen(` ALL MIGRATIONS SUCCEEDED `)} - ${performedMigrations.length} total`);
} else if (performedMigrations.length === 0 && failedMigrations.length > 0) {
console.error(`${chalk.black.bgRed(` ALL MIGRATIONS FAILED `)} - ${failedMigrations.length} total`);
} else {
console.error(
`${chalk.black.bgYellow(` SOME MIGRATIONS FAILED `)} - ${performedMigrations.length} succeeded, ${
failedMigrations.length
} failed`,
);
}
// exit with a non-zero code if any of the migrations failed
if (failedMigrations.length === 0) {
process.exit(0);
} else {
process.exit(1);
}
} catch (e) {
console.error(`${chalk.black.bgRed(` RUNNING MIGRATOR FAILED `)}`, e.stack);
} finally {
// gracefully close the connection
await migrator.close();
}
}
run().catch(e => console.error(chalk.black.bgRed(` RUNNING MIGRATOR FAILED `), e.stack));