Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
sql-migrations-core
Advanced tools
Database framework-agnostic solution for migrations with zero dependencies
Database framework-agnostic package for DIY migrations solutions with zero dependencies, written in TypeScript.
With sql-migrations-core
you can write sql migrations as you want. Use ANY database framework and prepare CLI as you feel comfortable.
Install with npm
:
npm i sql-migrations-core
Or with yarn
:
yarn add sql-migrations-core
...Or with pnpm
:
pnpm add sql-migrations-core
Minimal configuration looks like this:
const migrationsCore = MigrationsCore.create({
path: path.join('..', 'path/to/migrations/dir'),
sqlActions: {
async createMigrationTable() {
// TODO implement
},
async getMigrationsNames() {
// TODO implement
return [];
},
async migrateDown(migrations) {
// TODO implement
},
async migrateUp(migrations) {
// TODO implement
},
},
});
There is a trade-off - you can use any db framework, but you need to implement SQL queries by yourself. For example, with kysely:
import { db } from './database';
// Probably you'll want to move SQL logic to repository.
// In this example I'm just showing a minimal SQL implementation.
const migrationsCore = MigrationsCore.create({
path: path.join('/migrations'),
sqlActions: {
async createMigrationTable() {
await db.schema.createTable('__migrations')
.ifNotExists()
.addColumn('name', 'varchar', (cb) => cb.notNull().unique())
.addColumn('migrated_at', 'timestamp', (cb) =>
cb.notNull().defaultTo(sql`now()`)
)
.execute()
},
async getMigrationsNames() {
const records = await db.selectFrom('__migrations')
.select('name')
.execute();
return records.map(r => r.name);
},
async migrateDown(migrations) {
await db.transaction().execute(async (trx) => {
for (const migration of migrations) {
await trx.deleteFrom('__migrations')
.where({ name: migration.name })
.execute();
await sql`${migration.sql}`.execute(trx);
}
})
},
async migrateUp(migrations) {
await db.transaction().execute(async (trx) => {
for (const migration of migrations) {
await trx.insertInto('__migrations')
.values({ name: migration.name })
.execute();
await sql`${migration.sql}`.execute(trx);
}
})
},
},
});
See other examples here.
After initializing migrationsCore
you can use methods:
await migrationsCore.createFiles('example'); // creates blank sql files
await migrationsCore.up(); // one migration up
await migrationsCore.down(); // one migration down
await migrationsCore.sync(); // all pending up migrations
await migrationsCore.drop(); // all down migrations of executed migrations
await migrationsCore.toLatest(); // all pending up migrations from last executed
await migrationsCore.to('123-example'); // all up/down migrations between last executed and provided
await migrationsCore.status(); // get statuses of migrations (name and is synced)
Also, you can pass chunkSize
in some methods to run migrations by chunks in synchronous way:
await migrationsCore.sync(100); // runs migrations in chunks limit by 100
See API for more info.
Migrations core creates 2 migration files - up and down. It uses {timestamp}-${title}${postfix}
format (name of migration is {timestamp}-{title}
):
await migrationsCore.createFile('example');
// Will create something like this
// ./migrations/1718394484921-example.down.sql
// ./migrations/1718394484921-example.up.sql
There is no out-of-box CLI solution, so you need implement one by yourself. For example, create migration file:
import { migrationsCore } from './migrations-core';
import path from 'node:path';
const migrationsPath = path.join('./migrations');
migrationsCore.createFile(process.args[2]);
Usage:
node ./scripts/migrations-create.js example
See other CLI examples here.
There are several classes available to use:
MigrationsCore
- main class that gives access to all needed migration actions.LocalMigrations
- class used by MigrationsCore
. Hides all filesystem operations
to access local migrations. For advanced usage.StoredMigrations
- class used by MigrationsCore
. Hides all database operations
to run migrations and access metadata of executed migrations. For advanced usage.migrationsCore
objectThere are two ways to create instance of MigrationsCore
:
MigrationsCore.create()
methodnew MigrationsCore()
constructorcreate(config)
create()
method receives config object and returns instance of MigrationsCore
.
Actually it's constructor wrapper, which creates LocalMigrations
and StoredMigrations
by itself. Config options are passed to these objects. Config options:
path
- string, path to migrations directory. Used by LocalMigrations
.postfix
- object, optional, custom postfix for migrations files. Used by LocalMigrations
.postfix.up
- string, postfix for up migrations (default: .up.sql
).postfix.down
- string, postfix for down migrations (default: .down.sql
).sqlActions
- object, postfix for down migrations (default: .down.sql
). Used by StoredMigrations
.sqlActions.createMigrationTable
- function, used to create table before every StoredMigrations
action.
Recommended to use with IF NOT EXISTS
statement.sqlActions.migrateUp
- function, used to run up migrations. Receives array of migration objects.sqlActions.migrateDown
- function, used to run down migrations. Receives array of migration objects.sqlActions.getMigrationsNames
- function, used to get executed migrations names. Should return array of migration names.sqlActions.getLastMigrationName
- function, optional, used to get last executed migrations name.
Should return name or null. If function not provided - getMigrationsNames
used instead.Example:
const migrationsCore = MigrationsCore.create({
path: path.join(__dirname, '../migrations'),
postfix: {
up: '.custom.up.sql',
down: '.custom.down.sql',
},
sqlActions: {
createMigrationTable() { /* ... */ },
migrateUp(migrations) { /* ... */ },
migrateDown(migrations) { /* ... */ },
getMigrationsNames() { /* ... */ },
getLastMigrationName() { /* ... */ },
}
});
new MigrationsCore()
For options refer to Using create() method section.
Example:
const localMigrations = new LocalMigrations({
dirPath: path.join(__dirname, '../migrations'),
postfix: {
up: '.up.sql',
down: '.down.sql',
},
});
const storedMigrations = new StoredMigrations({
sqlActions: {
createMigrationTable() { /* ... */ },
migrateUp(migrations) { /* ... */ },
migrateDown(migrations) { /* ... */ },
getMigrationsNames() { /* ... */ },
getLastMigrationName() { /* ... */ },
}
});
const migrationsCore = new MigrationsCore(localMigrations, storedMigrations);
migrationsCore
objectSee migrations-core.interface.ts
localMigrations
objectSee local-migrations.interface.ts
storedMigrations
objectFAQs
Database framework-agnostic solution for migrations with zero dependencies
We found that sql-migrations-core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.