mygra

Agnostic database migration tool

Usage no npm install needed!

<script type="module">
  import mygra from 'https://cdn.skypack.dev/mygra';
</script>

README

Mygra

An agnostic Database migrations utility. Mygra provides a simple CLI to migrate up and down allowing you to define any database/connection to be passed to each up and down method.

Getting Started

For best use install mygra globally.

npm install mygra -g

Initialize

Navigate to your project and then initialize. You should then see a folder called mygra in the root of your project.

NOTE you can call this any time to reset or reinit it will not overwrite your config.js unless you call mygra init --force

mygra init

Define Config

Edit the mygra/config.js file and define the exported connection method to be passed to your up and down migration method.

The default example is Sqlite3 connection.

const dbPath = ':memory'; // or /your/db/path.
const connection = new Database(dbPath, (err) => {
  if (err) console.error(err);
});
module.exports = {
  connection,
};

Create a Migration

Run the following to generate a migration the resulting output will be located at mygra/migrations/1628915422808_user_table

mygra create 'user table'

Edit Migration

Open the newly created migration and edit your statements as required. In this case our config.js is exporting a Sqlite3 connection with which we use conn.run().

Where conn is our connection that we exported in our config.js. The connection is passed to each migration up/down handlers.

You may return a promise of boolean or use node style callback as shown here.

const name = 'user_table';
const description = '';

async function up(conn, cb) {
  const query = `CREATE TABLE IF NOT EXISTS user (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT NOT NULL UNIQUE,
  email TEXT NOT NULL UNIQUE,
  password TEXT NOT NULL
  )`;

  conn.run(query, (err) => {
    if (err) return cb(err);
    cb(null, true);
  });
}

async function down(conn, cb) {
  const query = `DROP TABLE IF EXISTS user`;

  return conn.run(query, (err) => {
    if (err) return cb(err);
    cb(null, true);
  });
}

module.exports = {
  name,
  description,
  up,
  down,
};

Run the Migration

Migrations can be run as up or down. If you wish to see a dry run simply add --preview to your command.

mygra up

Migrate down two migrations.

mygra down 2

Migrate up at user_table

mygra up user_table

Previewing a migration will show you all the steps it is about to take!

mygra user_table --preview

After running a migration you should see something like the following:

✔ Migration successful

  direction: down
  migrations: 1628915422808_user_table
  count: 1

Named Migrations

In the above example we called mygra up user_table. When using a named migration it is important to not that all migrations from the last or previous migration will be run also. If you wish to only step one at a time or a specified number of steps simply call up or down in the format of mygra up 1 or mygra down 3 where the numeric value is the number of steps you wish to traverse.

Consider the following where you've already run the user_table migration:

1628915422808_user_table
1628915422809_blog_table
1628915422810_acl_table
1628915422811_cart_table
1628915422812_invoice_table

If we now run the named mygration

mygra up cart_table

You will notice that not only does cart_table run but also blog_table and acl_table. This is because migrations are run in order of creation, hence why an epoch or timestamp is prepended to each name.

Revert Migrations

To undo the last migration and all steps that were run, could be one or multiple, just call the revert method. It's important to note that only one history step is preserved for simplicity. Once a new migration is run the previous revert steps will be overwritten.

mygra revert

Reset Migrations

You can also reset all migrations back to the begining or the first migration. Running reset will migrate down to the first migration step. Obviously this can be invasive and should be used with caution.

mygra reset

Out of Scope Error

You may receive an error complaining about the requested migration being out of scope. There are a few reasons this can happen.

  • There are no more migrations to run either you're at the most recent or you've run down to the first created.
  • You've requested a migration that's already be run. In our above example you ran the cart_table migration but then next call the acl_table migration but it's already been run.
  • The named migration you've specified does not exist.

Renamed Project

We base your config filename off of the package.json project name. If you rename your project you will end up with a default config.

If you move your project the path to your mygra migrations and templates likely in the root of your project will have a different. We can easily correct either of those.

If you've renamed your project run the following command and open the mygra config folder which is stored in your home directory:

mygra config --open

Find the old config file using the previous project name and open it in a text editor of your choice, then copy the JSON you see.

Either manually create a new config file with the proper project name or open the blank one that might have been created.

Next just paste in the config from the old config file and you should be good as new.

Moved Project

When you move a project the config file will still be found as it is named after your project name. However while it will find the correct config you'll find that migrations won't run as the migrations folder path stored in the config won't be correct.

To resolve this update the directory path using the command line.

mygra set directory '/path/to/project/mygra'

No Remove Migration

Why is there no remove migration?

This is by design as it's better to either migrate below the migration you wish to remove or to create a migration that overwrites or fixes the issue you are attempting to resolve. In short deleting migrations sort of defeats the purpose.

That said if you do need to remove a migration please ensure the following keys in your config do not reference the migration.

mygra config

You should then see something like the following. You will want to manually edit both the active and reverts keys so that the deleted migration is not referenced. When editing the active migration step you'll need to enter the step which you wish to be the newly active step. For the reverts key just remove reference to the deleted file from the array.

{
  initialized: true,
  directory: '/your/path/to/project_name/mygra',
  active: [ '1628915422808_user_table', 'up' ],
  reverts: [ [ '1628915422808_user_table' ], 'up' ],
  extension: '.js'
}

Do I need to Know the Timestamp Prefix

When working with named migrations you do NOT need to specify the timestamp prefix. Mygra will search the migrations and find the matching name.

It is important that migration names be unique even without the prefixed timestamp for this reason. Creating a migration even manually using a previous migration name will throw an error to ensure this is not done.

Can I Use Typescript Migrations

We're huge fans of Typescript, however in this case we decided to leave transpiling out of the equation. If you notice in the Mygra config there is an "extension" option. This is a placeholder in the event we decide to support Typescript in the future for migrations.

If interested in tinkering with this using ts-node for example feel free to do so and make a PR!!!

Ran Migration but Nothing Happens

If your database connection is failing you may see this behavior. Admittedly we need to spend some more time handling errors of this type and perhaps require a database connectivity check method be exported from mygra/config.js.

Simply run node mygra/config.js directly using Node where some sort of connectivity check is defined in your config.

Once successfully connecting Mygra should catch errors as you'd expect.

More Help

From your terminal run the help command which will show all options as well as a few command line examples.

mygra help

API Docs

See https://blujedis.github.io/mygra/

Change

See CHANGELOG.md

License

See LICENSE