{"id":16842,"library":"ley","title":"Driver-Agnostic Database Migrations","description":"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.","status":"active","version":"0.8.1","language":"javascript","source_language":"en","source_url":"https://github.com/lukeed/ley","tags":["javascript","typescript"],"install":[{"cmd":"npm install ley","lang":"bash","label":"npm"},{"cmd":"yarn add ley","lang":"bash","label":"yarn"},{"cmd":"pnpm add ley","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"A database driver is required for Ley to connect and interact with a database; pg is a common PostgreSQL client example. Other drivers like `mysql2` or `better-sqlite3` are also common.","package":"pg","optional":true},{"reason":"Recommended module loader for executing TypeScript migration files and `ley.config.ts` without needing to compile them first.","package":"tsm","optional":true}],"imports":[{"note":"Primarily for programmatic execution of migrations. ESM-only for direct import; use `require` for CommonJS.","wrong":"const { run } = require('ley')","symbol":"run","correct":"import { run } from 'ley'"},{"note":"The primary interface for most users. `npx` ensures the locally installed `ley` executable is used.","wrong":"node ley <command>","symbol":"Ley CLI","correct":"npx ley <command>"},{"note":"For migration files generated with `ley new --esm` or when the project is configured for ESM. Requires a module loader like `tsm` for TypeScript.","wrong":"exports.up = async function (sql) { /* ... */ }","symbol":"Migration File Exports (ESM)","correct":"export async function up(sql: Client) { /* ... */ }"},{"note":"Default for migration files generated without `--esm` or in CommonJS projects. Type definitions (`Client`) are for TypeScript files.","wrong":"export async function up(sql: Client) { /* ... */ }","symbol":"Migration File Exports (CJS)","correct":"exports.up = async function (sql) { /* ... */ }"}],"quickstart":{"code":"{\n  \"name\": \"my-ley-project\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Ley quickstart example\",\n  \"type\": \"module\", // Optional, but useful for ESM migrations\n  \"scripts\": {\n    \"migrate:new\": \"npx ley new\",\n    \"migrate:up\": \"node --loader tsm ley up\",\n    \"migrate:down\": \"node --loader tsm ley down\"\n  },\n  \"devDependencies\": {\n    \"ley\": \"^0.8.1\",\n    \"pg\": \"^8.11.3\",\n    \"tsm\": \"^2.3.0\"\n  }\n}\n\n// ley.config.ts\nimport { Pool } from 'pg';\nimport type { LeyDriver } from 'ley';\n\nconst driver: LeyDriver = {\n  connect: async () => {\n    const pool = new Pool({\n      connectionString: process.env.DATABASE_URL ?? 'postgresql://user:password@localhost:5432/mydb',\n    });\n    const client = await pool.connect();\n    return {\n      query: client.query.bind(client),\n      release: client.release.bind(client),\n      start: () => client.query('BEGIN'),\n      commit: () => client.query('COMMIT'),\n      rollback: () => client.query('ROLLBACK'),\n    };\n  },\n};\n\nexport default {\n  driver: driver,\n  migrations: './migrations',\n};\n\n// migrations/0000_initial.ts (generated by 'npx ley new initial --esm' then edited)\nimport type { PoolClient } from 'pg';\n\nexport async function up(sql: PoolClient) {\n  await sql.query(`\n    CREATE TABLE IF NOT EXISTS users (\n      id SERIAL PRIMARY KEY,\n      name VARCHAR(255) NOT NULL,\n      email VARCHAR(255) UNIQUE NOT NULL\n    );\n  `);\n  await sql.query(`\n    INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');\n  `);\n}\n\nexport async function down(sql: PoolClient) {\n  await sql.query(`DROP TABLE IF EXISTS users;`);\n}\n","lang":"typescript","description":"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."},"warnings":[{"fix":"Update any CLI usage or programmatic configurations from `--client` to `--driver`, and `opts.client` to `opts.driver`.","message":"In `v0.6.0`, the `--client` CLI option and `opts.client` programmatic option were removed. They have been replaced by `--driver` and `opts.driver` respectively, to better reflect the driver-agnostic nature of the tool.","severity":"breaking","affected_versions":">=0.6.0"},{"fix":"Always create new migrations for changes. Do not reorder, rename, or modify previously applied migration files. Use tools like `ley new` to ensure correct sequential or timestamped naming.","message":"Ley enforces an append-only, immutable task chain for migrations. Attempting to modify or insert migrations out of their original sequence can lead to errors and inconsistent database states, especially when collaborating or deploying.","severity":"gotcha","affected_versions":">=0.0.0"},{"fix":"Install the appropriate database driver for your project (e.g., `npm install pg`). Then, configure `ley.config.js` (or `ts`) to provide an instance of this driver or a custom driver object.","message":"Ley is driver-agnostic and does not bundle any database drivers. You must explicitly install and configure your desired database driver (e.g., `pg` for PostgreSQL, `mysql2` for MySQL) as a dependency in your project.","severity":"gotcha","affected_versions":">=0.0.0"},{"fix":"Install `tsm` (`npm install --save-dev tsm`). Update your `package.json` scripts to run `ley` commands using `node --loader tsm ley <command>`, for example: `\"migrate:up\": \"node --loader tsm ley up\"`.","message":"For TypeScript migrations or configuration files (`ley.config.ts`), `tsm` is the recommended module loader. Using `ts-node` might lead to unexpected behavior or require additional configuration due to how `ley` resolves modules.","severity":"gotcha","affected_versions":">=0.8.0"},{"fix":"Generate new migration files with `ley new --esm` to get ESM syntax. If you convert an existing project to ESM, update `ley.config.js` and migration files to use `export default` and `export function` syntax instead of `module.exports` and `exports.up`.","message":"When working with Node.js ES Modules, migration files and `ley.config.js` should use `import/export` syntax. If your project has `\"type\": \"module\"` in `package.json`, or you generate migrations with `ley new --esm`, ensure your files comply, otherwise, you may encounter CJS/ESM compatibility issues.","severity":"gotcha","affected_versions":">=0.7.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure the `migrations` directory exists in your project root or at the path specified in `ley.config.js` or via CLI options. Use `npx ley new <name>` to create your first migration file.","cause":"The configured 'migrations' directory is missing, empty, or the path is incorrect.","error":"Ley: No migrations found in './migrations'"},{"fix":"Install `tsm` (`npm install --save-dev tsm`) and update your `package.json` scripts to invoke `ley` using the `tsm` loader, e.g., `\"migrate:up\": \"node --loader tsm ley up\"`.","cause":"Node.js cannot directly execute TypeScript files without a module loader.","error":"TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension \".ts\" for <path>/ley.config.ts"},{"fix":"Install the required database driver (e.g., `npm install pg`). If using a custom driver, ensure it exports an object with a `connect` method that returns an object conforming to Ley's `LeyClient` interface (e.g., `query`, `release`, `start`, `commit`, `rollback`).","cause":"The database driver specified in `ley.config.js` or via CLI is not installed, or the provided driver object does not conform to Ley's driver interface.","error":"Error: Unknown driver: 'my-custom-driver-name' or TypeError: driver.connect is not a function"},{"fix":"Generate new migration files using `npx ley new --esm` and update existing ones to use ESM `export async function up(...) {}` syntax. Alternatively, if your project should be CJS, remove `\"type\": \"module\"` from `package.json`.","cause":"A migration file is using CommonJS `exports.up` syntax in a project or environment configured for Node.js ES Modules (e.g., `\"type\": \"module\"` in `package.json`).","error":"ReferenceError: exports is not defined in ES module scope"}],"ecosystem":"npm","meta_description":null}