Driver-Agnostic Database Migrations

0.8.1 · active · verified Wed Apr 22

Ley is a lightweight, driver-agnostic database migration tool for Node.js, currently at version 0.8.1. It provides both a command-line interface (CLI) and a programmatic API for managing database schema changes. Ley's core differentiators include its agnosticism towards specific database drivers (supporting `pg`, `postgres`, `mysql`, `mysql2`, `better-sqlite3`, and custom drivers without bundling them), its lightweight nature, and its transactional approach to migrations, ensuring atomicity for each change. It emphasizes working directly with your chosen driver's API, avoiding new abstractions, and enforces an append-only, immutable task chain for migrations to maintain database integrity across environments. Releases are consistent, with recent updates focusing on ESM support and improved TypeScript integration.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates setting up Ley with a PostgreSQL driver (`pg`) and TypeScript. It includes `package.json` scripts, a `ley.config.ts` file for driver configuration, and an example migration file (`0000_initial.ts`) with `up` and `down` functions using ESM syntax.

{
  "name": "my-ley-project",
  "version": "1.0.0",
  "description": "Ley quickstart example",
  "type": "module", // Optional, but useful for ESM migrations
  "scripts": {
    "migrate:new": "npx ley new",
    "migrate:up": "node --loader tsm ley up",
    "migrate:down": "node --loader tsm ley down"
  },
  "devDependencies": {
    "ley": "^0.8.1",
    "pg": "^8.11.3",
    "tsm": "^2.3.0"
  }
}

// ley.config.ts
import { Pool } from 'pg';
import type { LeyDriver } from 'ley';

const driver: LeyDriver = {
  connect: async () => {
    const pool = new Pool({
      connectionString: process.env.DATABASE_URL ?? 'postgresql://user:password@localhost:5432/mydb',
    });
    const client = await pool.connect();
    return {
      query: client.query.bind(client),
      release: client.release.bind(client),
      start: () => client.query('BEGIN'),
      commit: () => client.query('COMMIT'),
      rollback: () => client.query('ROLLBACK'),
    };
  },
};

export default {
  driver: driver,
  migrations: './migrations',
};

// migrations/0000_initial.ts (generated by 'npx ley new initial --esm' then edited)
import type { PoolClient } from 'pg';

export async function up(sql: PoolClient) {
  await sql.query(`
    CREATE TABLE IF NOT EXISTS users (
      id SERIAL PRIMARY KEY,
      name VARCHAR(255) NOT NULL,
      email VARCHAR(255) UNIQUE NOT NULL
    );
  `);
  await sql.query(`
    INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
  `);
}

export async function down(sql: PoolClient) {
  await sql.query(`DROP TABLE IF EXISTS users;`);
}

view raw JSON →