WIP: What's here is the end of Night #1
- driver support:
- complete test coverage
- complete documentation
-
Agnostic
Supportspostgres
,pg
,better-sqlite3
,sqlite
,mysql
, andmysql2
-
Lightweight
Does not include any driver dependencies. -
Transactional
Runs all migration files within a transaction for rollback safety. -
Familiar
Does not invent new syntax or abstractions.
You're always working directly with your driver of choice. -
Flexible
Find the CLI to restrictive? You may requireley
for your own scripting!
$ npm install --save-dev ley
Both Programmatic and CLI usages are supported.
You must have a migrations
directory created, preferably in your project's root.
Note: You may configure the target directory and location.
Your filenames within this directory determine the order of their execution.
Because of this, it's often recommended to prefix migrations with a timestamp or numerical sequence.
Numerical Sequence
/migrations
|-- 000-users.js
|-- 001-teams.js
|-- 002-seats.js
Note: You may create the next file via
ley new todos --length 3
wheretodos
is a meaningful name.
The above command will create themigrations/003-todos.js
filepath.
Timestamped
/migrations
|-- 1581323445-users.js
|-- 1581323453-teams.js
|-- 1581323458-seats.js
Note: You may create the next file via
ley new todos --timestamp
wheretodos
is a meaningful name.
The above command will create themigrations/1584389617-todos.js
filepath...or similar.
The order of your migrations is critically important!
Migrations must be treated as an append-only immutable task chain. Without this, there's no way to reliably rollback or recreate your database.
Example: (Above) You cannot apply/create
001-teams.js
after002-seats.js
has already been applied.
Doing so would force your teammates or database replicas to recreate "the world" in the wrong sequence.
This may not always pose a problem (eg, unrelated tasks) but it often does and soley
enforces this practice.
Lastly, each migration file must have an up
and a down
task.
These must be exported functions — async
okay! — and will receive your pre-installed client driver as its only argument:
exports.up = async function (DB) {
// with `pg` :: DB === pg.Client
await DB.query(`select * from users`);
// with `postgres` :: DB === sql``
await DB`select * from users`;
}
exports.down = async function (DB) {
// My pre-configured "undo" function
}
-
Add
ley
as one of yourpackage.json
scripts;"migrate"
, for example:// package.json { "scripts": { "migrate": "ley" } }
-
Invoke
ley up
to apply new migrations, orley down
to rollback previous migrations.$ npm run migrate up $ yarn migrate up
Note: See API for documentation
With programmatic/scripting usage, you will not inherit any of ley
's CLI tooling, which includes all colors and error formatting. Instead, you must manually catch & handle all thrown Errors.
const ley = require('ley');
const successes = await ley.up({ ... });
Returns: Promise<string[]>
Returns a list of the relative filenames (eg, 000-users.js
) that were successfully applied.
Type: string
Default: .
A target location to treat as the current working directory.
Note: This value is
path.resolve()
d from the currentprocess.cwd()
location.
Type: string
Default: migrations
The directory (relative to opts.cwd
) to find migration files.
Type: string
Default: undefined
The name of your desired client driver; for example, pg
.
When unspecified, ley
searches for all supported client drivers in this order:
['postgres', 'pg']; // TODO: more
Type: object
Default: undefined
A configuration object for your client driver to establish a connection.
When unspecified, ley
assumes that your client driver is able to connect through process.env
variables.
Note: The
ley
CLI will search for aley.config.js
config file (configurable).
If found, this file may contain an object or a function that resolves to your config object.
Type: boolean
Default: false
Enable to apply only one migration file's up
task.
By default, all migration files will be queue for application.
Returns: Promise<string[]>
Returns a list of the relative filenames (eg, 000-users.js
) that were successfully applied.
Type: string
Default: .
A target location to treat as the current working directory.
Note: This value is
path.resolve()
d from the currentprocess.cwd()
location.
Type: string
Default: migrations
The directory (relative to opts.cwd
) to find migration files.
Type: string
Default: undefined
The name of your desired client driver; for example, pg
.
When unspecified, ley
searches for all supported client drivers in this order:
['postgres', 'pg']; // TODO: more
Type: boolean
Default: false
Enable to apply all migration files' down
task.
By default, only the most recently-applied migration file is invoked.
Returns: Promise<string>
Returns the newly created relative filename (eg, 000-users.js
).
Type: string
Required. The name of the file to be created.
Note: A prefix will be prepended based on
opts.timestamp
andopts.length
values.
The.js
extension will be applied unless your input already has an extension.
Type: boolean
Default: false
Should the migration file have a timestamped prefix?
If so, will use Date.now()
floored to the nearest second.
Type: number
Default: 5
When not using a timestamped prefix, this value controls the prefix total length.
For example, 00000-users.js
will be followed by 00001-teams.js
.
Type: string
Default: .
A target location to treat as the current working directory.
Note: This value is
path.resolve()
d from the currentprocess.cwd()
location.
Type: string
Default: migrations
The directory (relative to opts.cwd
) to find migration files.
MIT © Luke Edwards