UNPKG

lipgrate

Version:

Lipgrate is a clean and safe migration toolkit for SQL databases. Designed to be readable, minimal, and powerful.

332 lines (253 loc) 11.4 kB
# Lipgrate: A Lightweight Database Migration Tool Lipgrate is a simple, declarative, and framework-agnostic database migration tool designed for Node.js. It helps you manage your database schema evolution with ease, using plain JavaScript files to define changes. ## Installation Install Lipgrate globally to use it as a command-line tool: ```bash npm install -g lipgrate ``` ## Quick Start 1. **Initialize your project:** This creates an interactive `migrator.config.js` file with configurations for PostgreSQL, MySQL, and SQLite. It also creates a `migrations` directory containing database-specific subdirectories (`/postgresql`, `/mysql`, `/sqlite`) and helpful examples. ```bash node src/cli.js init ``` 2. **Configure your database:** Open `migrator.config.js`, choose the configuration object for your database, and assign it to `module.exports`. Then, update the connection details. 3. **Create a migration:** ```bash node src/cli.js create your_migration_name ``` 4. **Run migrations:** ```bash node src/cli.js migrate ``` 5. **Roll back the last migration:** ```bash node src/cli.js rollback ``` 6. **Check status:** ```bash node src/cli.js status ``` 7. **(Optional) Seed your database:** ```bash node src/cli.js seed ``` 8. **(Optional) Reset for development:** ```bash node src/cli.js reset ``` ## Features - **Declarative Migrations**: Define schema changes using simple JavaScript objects. - **Multi-Operation Migrations**: Run multiple schema changes in a single migration file. - **Database Agnostic**: Supports PostgreSQL, MySQL, and SQLite. - **Clear Logging**: Get human-readable feedback for every operation during migration and rollback. - **Intelligent Error Handling**: Get helpful "Did you mean?" suggestions for typos in data type names. - **Safe Dry Runs**: Preview the SQL for any migration or rollback without executing it using the `--dry-run` flag. ## Installation ```bash npm install -g lipgrate # Or use it as a local dev dependency npm install --save-dev lipgrate ``` ## Usage ### 1. Create a Migration Generate a new, database-specific migration file. Lipgrate reads your `migrator.config.js` to determine the database client (e.g., `postgresql`, `mysql`, `sqlite`) and provides a template with relevant examples for that database. ```bash lipgrate create <migration_name> ``` **Example:** ```bash # Assuming your config client is 'postgresql' lipgrate create create_users_table ``` This will create a file inside the correct directory (e.g., `migrations/postgresql/`) containing a PostgreSQL-specific template, ready for you to edit. ### 2. Run Migrations Apply all pending migrations. ```bash node src/cli.js migrate ``` ### 3. Rollback Migrations Roll back the most recent batch of migrations. ```bash node src/cli.js rollback ``` ### 4. Check Migration Status See which migrations have been applied and which are pending. ```bash node src/cli.js status ``` ### 5. Seed the Database Populate your database with initial data. Lipgrate executes all `.js` files in the `migrations/seeds` directory. It is recommended to prefix seed files with numbers to control execution order (e.g., `01_users.js`). ```bash node src/cli.js seed ``` **Example `migrations/seeds/01_products.js`:** ```javascript module.exports = { async run(db) { // It's good practice to log what your seed is doing. console.log('Seeding initial products...'); // Use parameterized queries to prevent SQL injection. await db.query( 'INSERT INTO products (name, price) VALUES (?, ?), (?, ?)', ['Laptop Pro', 1499.99, 'Wireless Mouse', 49.99] ); console.log('Finished seeding products.'); } }; ``` ### 6. Reset the Database **⚠️ Destructive Operation.** Drops all tables in the database and then re-runs all migrations. Ideal for resetting a development database. ```bash node src/cli.js reset ``` ### Dry Run Mode To ensure safety and predictability, you can preview the SQL commands for any migration or rollback without applying them to the database. Use the `--dry-run` flag with the `migrate` or `rollback` commands. **Example:** ```sh $ node src/cli.js migrate --dry-run ``` This will output all the SQL statements that would have been executed, prefixed with `[DRY RUN]`, allowing you to review changes before they go live. ## Example Migration File A migration file exports `up` and `down` objects. You can define a single operation or an array of operations to be executed in order. ```javascript // Defines the UP migration action. exports.up = [ { createTable: { name: 'users', columns: { id: 'increments', username: 'string:unique:notNullable', email: 'string(191):unique:notNullable', status: "enum('active', 'pending', 'banned'):default('pending')", bio: 'text' }, options: { timestamps: true // Adds created_at and updated_at } } }, { addIndex: { table: 'users', columns: 'email', name: 'users_email_idx' } } ]; // Defines the DOWN migration action. exports.down = [ { dropIndex: { table: 'users', name: 'users_email_idx' } }, { dropTable: 'users' } ]; ``` ## Configuration Create a `migrator.config.js` file in your project root to define your database connections. You can specify different environments, such as `development` and `production`. **PostgreSQL Example:** ```javascript module.exports = { development: { client: 'postgresql', connection: { host: 'localhost', port: 5432, user: 'your_user', password: 'your_password', database: 'your_dev_db' } } }; ``` **SQLite Example:** ```javascript module.exports = { development: { client: 'sqlite', connection: { filename: './lipgrate.db' } } }; ``` ## Supported Data Types ### PostgreSQL - **Numeric**: `smallint` (alias `int2`), `integer` (aliases `int`, `int4`), `bigint` (alias `int8`), `decimal`, `numeric`, `real` (alias `float4`), `double precision` (alias `float8`). - **Auto-Incrementing**: `smallserial` (alias `serial2`), `serial` (alias `serial4`), `bigserial` (alias `serial8`), `increments` (alias for `serial`). - **Monetary**: `money`. - **Character**: `character(n)` (alias `char(n)`), `character varying(n)` (alias `varchar(n)`), `text`. - **Binary**: `bytea`. - **Date/Time**: `timestamp`, `timestamp with time zone` (alias `timestamptz`), `date`, `time`, `time with time zone` (alias `timetz`), `interval`. - **Boolean**: `boolean` (alias `bool`). - **Geometric**: `point`, `line`, `lseg`, `box`, `path`, `polygon`, `circle`. - **Network Address**: `cidr`, `inet`, `macaddr`, `macaddr8`. - **Bit String**: `bit(n)`, `bit varying(n)` (alias `varbit(n)`). - **Text Search**: `tsvector`, `tsquery`. - **UUID**: `uuid`. - **XML**: `xml`. - **JSON**: `json`, `jsonb`. - **Internal**: `pg_lsn`, `pg_snapshot`, `txid_snapshot`. - **Arrays**: Any data type can be made into an array, e.g., `text[]`, `integer[]`. ### SQLite SQLite uses a more general, dynamic type system referred to as type affinity. When you specify a type, SQLite converts and stores it as one of the following storage classes: - **TEXT**: For `string`, `char`, `text`, `date`, `datetime`, `json`, `uuid`. - **INTEGER**: For `integer`, `bigInteger`, `boolean`. - **REAL**: For `float`, `double`, `decimal`. - **BLOB**: For `binary` data. - **INTEGER PRIMARY KEY AUTOINCREMENT**: For `increments`. ### MySQL ## Supported Schema Operations The level of support for schema operations can vary between database systems due to their differing capabilities. | Operation | PostgreSQL | MySQL | SQLite | Notes | | ----------------- | :--------: | :---: | :----: | ----------------------------------------------------- | | `createTable` | ✅ | ✅ | ✅ | | | `dropTable` | ✅ | ✅ | ✅ | | | `renameTable` | ✅ | ✅ | ✅ | | | `addColumn` | ✅ | ✅ | ✅ | | | `dropColumn` | ✅ | ✅ | ✅ | | | `renameColumn` | ✅ | ✅ | ✅ | | | `alterColumn` | ✅ | ✅ | ❌ | SQLite has limited support; recreate table instead. | | `addIndex` | ✅ | ✅ | ✅ | | | `dropIndex` | ✅ | ✅ | ✅ | | | `addForeignKey` | ✅ | ✅ | ❌ | Add FKs during `createTable` in SQLite. | | `dropForeignKey` | ✅ | ✅ | ❌ | Recreate table to drop FKs in SQLite. | * ✅ = Supported * ❌ = Not Supported (or requires manual table recreation) ## Intelligent Error Handling To improve the developer experience, Lipgrate includes a smart error-handling mechanism. If you accidentally make a typo when specifying a data type in your migration, Lipgrate will detect it and provide a helpful suggestion instead of a generic SQL error. **Example:** If you write `'inteeger'` instead of `'integer'`, you will see the following error: `✖ Migration failed: Unknown data type 'inteeger'. Did you mean 'integer'?'` This helps you quickly identify and fix schema definition errors. ## Column Definitions A column definition is a string composed of a type and optional modifiers, separated by colons (`:`). - **Syntax**: `'type(args):modifier1:modifier2'` - **Examples**: - `'increments'` - `'string:notNullable'` - `'string(100):unique'` - `'decimal(10, 2):default(0.00)'` - `"enum('a', 'b'):default('a')"` ## Contributing and Issues Contributions are welcome! If you find a bug, have a feature request, or want to contribute to the code, please feel free to open an issue or submit a pull request on the [GitHub repository](https://github.com/alifxploid/lipgrate). When reporting an issue, please include: - A clear description of the issue. - The steps to reproduce it. - Your `lipgrate` version and Node.js version. ### Supported Data Types (MySQL) This list covers the internal data type aliases. Lipgrate will also pass through any valid MySQL data type definition directly. - **Numeric**: `increments`, `tinyInteger`, `smallInteger`, `mediumInteger`, `integer`, `bigInteger`, `decimal`, `float`, `double`, `real`, `bit`, `boolean`, `serial` - **Date & Time**: `date`, `datetime`, `timestamp`, `time`, `year` - **String & Text**: `string` (for VARCHAR), `char`, `tinyText`, `text`, `mediumText`, `longText`, `enum(...)`, `set(...)` - **Binary & Blob**: `binary`, `varbinary`, `tinyBlob`, `blob`, `mediumBlob`, `longBlob` - **Spatial**: `geometry`, `point`, `linestring`, `polygon`, `multipoint`, `multilinestring`, `multipolygon`, `geometrycollection` (all passed as-is) - **JSON**: `json` - **Other**: `uuid` (as CHAR(36))