{"_id":"@atith/mysql-data-migrator","_rev":"5-0f705e1293272bee16a989a3deb3d9de","name":"@atith/mysql-data-migrator","dist-tags":{"latest":"1.4.0"},"versions":{"1.0.0":{"name":"@atith/mysql-data-migrator","version":"1.0.0","keywords":["mysql","migration","data-migration","sql","nodejs","typescript"],"author":{"name":"Atith N"},"license":"MIT","_id":"@atith/mysql-data-migrator@1.0.0","maintainers":[{"name":"atith","email":"atith91098@gmail.com"}],"dist":{"shasum":"ddd5e45e8a64abd5e1638146f203a8b15a34c117","tarball":"https://registry.npmjs.org/@atith/mysql-data-migrator/-/mysql-data-migrator-1.0.0.tgz","fileCount":6,"integrity":"sha512-vF9CBiMl4ZuyQtM6X+IMU7FjkX8Y+9vyMn/PjBH/8Nd19XfmA6dq8d2SheuKBazJDBNvWy3Df96GeY7rvvw4sA==","signatures":[{"sig":"MEUCIQDYGDdbZg+3MAFZMF+emt+E5ItOFzGRF/LrKdLrf5AaFQIgFek0cZWTt4Brq7O3JO3b/0ghaRdkMW9eBV01eZxh35A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10874},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"dev":"ts-node src/index.ts","build":"tsup src/index.ts --format cjs,esm --dts","prepublishOnly":"npm run build"},"_npmUser":{"name":"atith","email":"atith91098@gmail.com"},"_npmVersion":"10.8.2","description":"A lightweight MySQL data migration runner using raw SQL queries","directories":{},"_nodeVersion":"20.19.6","dependencies":{"mysql2":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mysql-data-migrator_1.0.0_1779696830024_0.7707477381756878","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@atith/mysql-data-migrator","version":"1.1.0","keywords":["mysql","migration","data-migration","sql","nodejs","typescript"],"author":{"name":"Atith N"},"license":"MIT","_id":"@atith/mysql-data-migrator@1.1.0","maintainers":[{"name":"atith","email":"atith91098@gmail.com"}],"dist":{"shasum":"54c8de2ab25c47cb1471f39fd4e43b4a9c0afffc","tarball":"https://registry.npmjs.org/@atith/mysql-data-migrator/-/mysql-data-migrator-1.1.0.tgz","fileCount":6,"integrity":"sha512-VNGYsnIY7PSRHKmyoQ+O2dTDHxw5JvJWFgiKU77fLIONEgWZo85YXi8IBEeILKwMDjfxVUmomvAkt8k74IgOpw==","signatures":[{"sig":"MEUCIAObiwUjmpX8qKfdIpPn0UrIDmohRubcNzYaKsl1/roaAiEA10J8psEfeRpes66T7vmJtlPwD2gMfOPBnm93nceP5Mc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22614},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"dev":"ts-node src/index.ts","build":"tsup src/index.ts --format cjs,esm --dts","prepublishOnly":"npm run build"},"_npmUser":{"name":"atith","email":"atith91098@gmail.com"},"_npmVersion":"10.8.2","description":"A lightweight MySQL data migration runner using raw SQL queries","directories":{},"_nodeVersion":"20.19.6","dependencies":{"mysql2":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mysql-data-migrator_1.1.0_1779712847502_0.20603112946136748","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@atith/mysql-data-migrator","version":"1.2.0","keywords":["mysql","migration","data-migration","sql","nodejs","typescript"],"author":{"name":"Atith N"},"license":"MIT","_id":"@atith/mysql-data-migrator@1.2.0","maintainers":[{"name":"atith","email":"atith91098@gmail.com"}],"dist":{"shasum":"6f26aeb5170a6cde75959a064ec1087c72a41079","tarball":"https://registry.npmjs.org/@atith/mysql-data-migrator/-/mysql-data-migrator-1.2.0.tgz","fileCount":6,"integrity":"sha512-DJSWTTmHy1ftRHtWw9EfgM2Ppj9wOOIu/W8YnxfG5FK1jyo+2S51vrLIbSXkQseXWprGzdjsvKTEdlYT3T+i6w==","signatures":[{"sig":"MEUCIQCxNdx+5+kugNbtv5y1YDtk+ryYNIOuNNqZ3q+Dn1chwwIgTtJReApbsTheOyJaIcwleJyBVuiS9eyhR9YKOWCCBe0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25631},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"dev":"ts-node src/index.ts","build":"tsup src/index.ts --format cjs,esm --dts","prepublishOnly":"npm run build"},"_npmUser":{"name":"atith","email":"atith91098@gmail.com"},"_npmVersion":"10.8.2","description":"A lightweight MySQL data migration runner using raw SQL queries","directories":{},"_nodeVersion":"20.19.6","dependencies":{"mysql2":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mysql-data-migrator_1.2.0_1779972589254_0.25958653107997076","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@atith/mysql-data-migrator","version":"1.3.0","keywords":["mysql","migration","data-migration","sql","nodejs","typescript"],"author":{"name":"Atith N"},"license":"MIT","_id":"@atith/mysql-data-migrator@1.3.0","maintainers":[{"name":"atith","email":"atith91098@gmail.com"}],"dist":{"shasum":"a7d416dcc934d1c8f6b6b5524f7f0a6733f467b7","tarball":"https://registry.npmjs.org/@atith/mysql-data-migrator/-/mysql-data-migrator-1.3.0.tgz","fileCount":6,"integrity":"sha512-gUcugoNvXjCvOgnyxvnxOhmPXsF+3Mk/dnXbbwi56lijdhIMgO+ooWt6IhEpBN6R9Ok57b/ivcxtoMyfTdjBSw==","signatures":[{"sig":"MEUCIBarRAMY0sZPUNwjjs7yk61V2rwm223aegdO49dypn7tAiEAiuNo/srwpEtrq+4lHf0veEoFybG+HBIh2v4OtDu9JNQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":38340},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"dev":"ts-node src/index.ts","build":"tsup src/index.ts --format cjs,esm --dts","prepublishOnly":"npm run build"},"_npmUser":{"name":"atith","email":"atith91098@gmail.com"},"_npmVersion":"10.8.2","description":"A lightweight MySQL data migration runner using raw SQL queries","directories":{},"_nodeVersion":"20.19.6","dependencies":{"mysql2":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mysql-data-migrator_1.3.0_1780040668938_0.6544199846612306","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@atith/mysql-data-migrator","version":"1.4.0","description":"A lightweight MySQL data migration runner using raw SQL queries","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsup src/index.ts --format cjs,esm --dts","dev":"ts-node src/index.ts","prepublishOnly":"npm run build"},"keywords":["mysql","migration","data-migration","database-migration","db-migration","schema-migration","sql","raw-sql","mysql-migration","mysql-migrator","migration-runner","data-migrator","database-migrator","nodejs","node","typescript","javascript","mysql2","migrations","database","database-tool","sql-migration","version-control","migration-history","rollback","rollbacks"],"author":{"name":"Atith N"},"license":"MIT","dependencies":{"mysql2":"^3.0.0"},"devDependencies":{"@types/node":"^20.0.0","tsup":"^8.0.0","typescript":"^5.0.0"},"_id":"@atith/mysql-data-migrator@1.4.0","_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-ZeAtehaSd5L844w7TUy0SQ9bWE+lIiqHFfPyXwyxGWCOtXbW2RVNTSdqNpIGRcFhMc65aVJI6yWHeJd43ai/7w==","shasum":"48e8b09ef74678b694e39f970862eaee4d0dde62","tarball":"https://registry.npmjs.org/@atith/mysql-data-migrator/-/mysql-data-migrator-1.4.0.tgz","fileCount":6,"unpackedSize":47027,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCtkkZ8yBIRB/d56+8tDMlY3TMijk76ilHPNigQpQBHJwIgW03p6gQnHhMBp71RHt8MV4E7vj6Tyr7XlI2aj8hEe6k="}]},"_npmUser":{"name":"atith","email":"atith91098@gmail.com"},"directories":{},"maintainers":[{"name":"atith","email":"atith91098@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mysql-data-migrator_1.4.0_1780404197291_0.7921699705356968"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-25T08:13:49.919Z","modified":"2026-06-02T12:43:17.549Z","1.0.0":"2026-05-25T08:13:50.173Z","1.1.0":"2026-05-25T12:40:47.635Z","1.2.0":"2026-05-28T12:49:49.403Z","1.3.0":"2026-05-29T07:44:29.066Z","1.4.0":"2026-06-02T12:43:17.435Z"},"author":{"name":"Atith N"},"license":"MIT","keywords":["mysql","migration","data-migration","database-migration","db-migration","schema-migration","sql","raw-sql","mysql-migration","mysql-migrator","migration-runner","data-migrator","database-migrator","nodejs","node","typescript","javascript","mysql2","migrations","database","database-tool","sql-migration","version-control","migration-history","rollback","rollbacks"],"description":"A lightweight MySQL data migration runner using raw SQL queries","maintainers":[{"name":"atith","email":"atith91098@gmail.com"}],"readme":"# MySQL Data Migrator\n\nA lightweight MySQL data migration runner for MySQL databases using raw SQL queries, migration history tracking, folder-based migrations, existing connection support, and rollback support.\n\nThis package is mainly designed for data migrations such as:\n\n* `UPDATE`\n* `INSERT`\n* `DELETE`\n* Backfills\n* Cleanup operations\n* Data normalization\n* Moving data between tables\n\nYou can also use it for schema migrations like `ALTER TABLE`, but use them carefully because MySQL may auto-commit DDL statements.\n\nUse this package at your own risk while using schema operations like `ALTER TABLE`, `DROP TABLE`, `TRUNCATE TABLE`, etc.\n\n---\n\nNote: For this package, using `mysql2` is recommended for proper functioning of this package. Usage of any deprecated versions of mysql can lead to performance issues.\n\n## Install\n\n```bash\nnpm install @atith/mysql-data-migrator\n```\n\n```bash\nnpm install mysql2\n```\n\n---\n\n## Features\n\n* Supports raw SQL migrations\n* Supports parameterized SQL queries\n* Supports migration array\n* Supports migration folder path\n* Supports `.migration.js` files\n* Automatically derives migration name from file name if `name` is not provided\n* Supports `up` migrations\n* Supports `down` rollback migrations\n* Supports rollback using `steps`\n* Supports rollback using specific migration file paths\n* Supports creating a MySQL connection from a connection config object\n* Supports passing an existing MySQL connection instance\n* Tracks migration operation as `up` or `down`\n* Updates timestamp when the same migration name and operation already exists\n* Uses a migration history table\n\n---\n\n## Connection Configuration\n\nYou can pass the database connection in two ways.\n\n### 1. Pass Connection Config Object\n\nThis is the default and simplest way.\n\nThe package creates and manages the MySQL connection internally.\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\n\nasync function main() {\n  const result = await runMigrations({\n    connection: {\n      host: \"localhost\",\n      user: \"root\",\n      password: \"password\",\n      database: \"test_db\",\n      port: 3306,\n    },\n\n    migrationTableName: \"_data_migrations\",\n    migrations: \"./migrations\",\n  });\n\n  console.log(result);\n}\n\nmain().catch(console.error);\n```\n\n### 2. Pass Existing MySQL Connection\n\nYou can also create the MySQL connection yourself and pass the connection instance to the package.\n\nThis is useful when:\n\n* Your app already manages the database connection\n* You want to reuse an existing connection\n* You want more control over connection creation\n* You want to use custom `mysql2` connection options\n\n#### CommonJS Example\n\nUse this when your project is using CommonJS.\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\nconst mysql = require(\"mysql2/promise\");\n\nasync function main() {\n  const connection = await mysql.createConnection({\n    host: \"localhost\",\n    user: \"root\",\n    password: \"password\",\n    database: \"test_db\",\n    port: 3306,\n  });\n\n  try {\n    const result = await runMigrations({\n      connection,\n      migrationTableName: \"_data_migrations\",\n      migrations: \"./migrations\",\n    });\n\n    console.log(result);\n  } finally {\n    await connection.end();\n  }\n}\n\nmain().catch(console.error);\n```\n\n#### ES Module Example\n\nUse this when your project has `\"type\": \"module\"` in `package.json`, or when your file is running as an ES module.\n\n```js\nimport { runMigrations } from \"@atith/mysql-data-migrator\";\nimport mysql from \"mysql2/promise\";\n\nconst connection = await mysql.createConnection({\n  host: \"localhost\",\n  user: \"root\",\n  password: \"password\",\n  database: \"test_db\",\n  port: 3306,\n});\n\ntry {\n  const result = await runMigrations({\n    connection,\n    migrationTableName: \"_data_migrations\",\n    migrations: \"./migrations\",\n  });\n\n  console.log(result);\n} finally {\n  await connection.end();\n}\n```\n\n> Important: `mysql.createConnection()` returns a promise, so always use `await`.\n\nCorrect:\n\n```js\nconst connection = await mysql.createConnection({\n  host: \"localhost\",\n  user: \"root\",\n  password: \"password\",\n  database: \"test_db\",\n  port: 3306,\n});\n```\n\nIncorrect:\n\n```js\nconst connection = mysql.createConnection({\n  host: \"localhost\",\n  user: \"root\",\n  password: \"password\",\n  database: \"test_db\",\n  port: 3306,\n});\n```\n---\n\n## Migration History Table\n\nBy default, the package creates a migration table named: \"_data_migrations\"\n\nThe table stores migration history like this:\n\n```sql\nCREATE TABLE IF NOT EXISTS `_data_migrations` (\n  id INT AUTO_INCREMENT PRIMARY KEY,\n  name VARCHAR(255) NOT NULL,\n  operation ENUM('up', 'down') NOT NULL,\n  executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n  UNIQUE KEY unique_migration_operation (name, operation)\n);\n```\n\nThe `operation` column stores whether the migration was executed as:\n\n```txt\nup\ndown\n```\n\nIf the same `name` and `operation` already exist, the package updates the `executed_at` timestamp instead of inserting a duplicate record.\n\nExample:\n\n| id | name                  | operation | executed_at         |\n| -: | --------------------- | --------- | ------------------- |\n|  1 | 001_add_status_column | up        | 2026-05-29 10:00:00 |\n|  2 | 001_add_status_column | down      | 2026-05-29 10:05:00 |\n\n---\n\n## Basic Usage With Migration Array\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\n\nasync function main() {\n  const result = await runMigrations({\n    connection: {\n      host: \"localhost\",\n      user: \"root\",\n      password: \"password\",\n      database: \"test_db\",\n      port: 3306,\n    },\n\n    migrationTableName: \"_data_migrations\",\n\n    migrations: [\n      {\n        name: \"001_trim_users_test_names\",\n        up: {\n          sql: `\n            UPDATE users_test\n            SET name = TRIM(name)\n            WHERE name IS NOT NULL\n          `,\n          params: [],\n        },\n      },\n    ],\n  });\n\n  console.log(result);\n}\n\nmain().catch(console.error);\n```\n\n---\n\n## Migration File Usage\n\nYou can also pass a folder path instead of an array.\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\n\nasync function main() {\n  const result = await runMigrations({\n    connection: {\n      host: \"localhost\",\n      user: \"root\",\n      password: \"password\",\n      database: \"test_db\",\n      port: 3306,\n    },\n\n    migrationTableName: \"_data_migrations\",\n\n    migrations: \"./migrations\",\n  });\n\n  console.log(result);\n}\n\nmain().catch(console.error);\n```\n\nFolder structure:\n\n```txt\nproject-root/\n  migrations/\n    001_add_status_column.migration.js\n    002_update_status_values.migration.js\n```\n\nOnly files ending with `.migration.js` will be executed and rest of them will be excluded.\n\nMigration files are loaded in sorted order, so use a number prefix:\n\n```txt\n001_first_migration.migration.js\n002_second_migration.migration.js\n003_third_migration.migration.js\n```\n\n---\n\n## Example Migration File\n\nCreate:\n\n```txt\nmigrations/001_add_status_column.migration.js\n```\n\n```js\nmodule.exports = {\n  up: {\n    sql: `\n      ALTER TABLE users_test\n      ADD COLUMN status VARCHAR(50) DEFAULT 'active'\n    `,\n    params: [],\n  },\n\n  down: {\n    sql: `\n      ALTER TABLE users_test\n      DROP COLUMN status\n    `,\n    params: [],\n  },\n};\n```\n\nIf `name` is not provided, the package automatically uses the file name without `.migration.js`.\n\nFor this file:\n\n```txt\n001_add_status_column.migration.js\n```\n\nThe migration name becomes:\n\n```txt\n001_add_status_column\n```\n\nYou can also explicitly provide a name:\n\n```js\nmodule.exports = {\n  name: \"001_add_status_column\",\n\n  up: {\n    sql: `\n      ALTER TABLE users_test\n      ADD COLUMN status VARCHAR(50) DEFAULT 'active'\n    `,\n    params: [],\n  },\n\n  down: {\n    sql: `\n      ALTER TABLE users_test\n      DROP COLUMN status\n    `,\n    params: [],\n  },\n};\n```\n\n---\n\n## Running Up Migrations\n\n`runMigrations()` executes the `up` query of pending migrations.\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\n\nawait runMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n});\n```\n\nBehavior:\n\n| Latest operation           | What happens    |\n| -------------------------- | --------------- |\n| No record exists           | Runs `up`       |\n| Latest operation is `down` | Runs `up` again |\n| Latest operation is `up`   | Skips migration |\n\nExample result:\n\n```json\n{\n  \"executed\": [\n    {\n      \"name\": \"001_add_status_column\",\n      \"status\": \"success\"\n    }\n  ],\n  \"skipped\": [],\n  \"failed\": []\n}\n```\n\n---\n\n## Running Down Migrations Using Steps\n\n`rollbackMigrations()` executes the `down` query.\n\n```js\nconst { rollbackMigrations } = require(\"@atith/mysql-data-migrator\");\n\nawait rollbackMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n  steps: 1,\n});\n```\n\n`steps: 1` rolls back the latest applied migration.\n\nExample:\n\n```txt\n001_add_status_column\n002_update_status_values\n```\n\nIf both were applied, then:\n\n```js\nsteps: 1\n```\n\nrolls back:\n\n```txt\n002_update_status_values\n```\n\nExample result:\n\n```json\n{\n  \"rolledBack\": [\n    {\n      \"name\": \"002_update_status_values\",\n      \"status\": \"rolled_back\"\n    }\n  ],\n  \"skipped\": [],\n  \"failed\": []\n}\n```\n\n---\n\n## Running Down Migrations Using Specific Files\n\nYou can rollback specific migration files using `rollbackFiles`.\n\n```js\nconst { rollbackMigrations } = require(\"@atith/mysql-data-migrator\");\n\nawait rollbackMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n\n  rollbackFiles: [\n    \"./migrations/002_update_status_values.migration.js\",\n  ],\n});\n```\n\nThis rolls back only:\n\n```txt\n002_update_status_values\n```\n\nIf both `steps` and `rollbackFiles` are provided, `rollbackFiles` takes priority and `steps` is ignored.\n\nExample:\n\n```js\nawait rollbackMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n  steps: 5,\n\n  rollbackFiles: [\n    \"./migrations/002_update_status_values.migration.js\",\n  ],\n});\n```\n\nThe above will rollback only:\n\n```txt\n002_update_status_values\n```\n\nIt will not rollback 5 migrations.\n\n---\n\n## Example Migration With Params\n\n```js\nmodule.exports = {\n  up: {\n    sql: `\n      UPDATE users_test\n      SET name = ?\n      WHERE name = ''\n    `,\n    params: [\"UNKNOWN\"],\n  },\n\n  down: {\n    sql: `\n      UPDATE users_test\n      SET name = ?\n      WHERE name = 'UNKNOWN'\n    `,\n    params: [\"\"],\n  },\n};\n```\n\nThe `?` placeholders are filled using the `params` array.\n\nThis is safer than directly injecting values into the SQL string.\n\n---\n\n## Full Test Example\n\nCreate test table:\n\n```sql\nDROP TABLE IF EXISTS _data_migrations;\nDROP TABLE IF EXISTS users_test;\n\nCREATE TABLE users_test (\n  id INT AUTO_INCREMENT PRIMARY KEY,\n  name VARCHAR(100)\n);\n\nINSERT INTO users_test (name)\nVALUES\n  ('Atith'),\n  ('John'),\n  ('Mike');\n```\n\nCreate migration:\n\n```txt\nmigrations/001_add_status_column.migration.js\n```\n\n```js\nmodule.exports = {\n  up: {\n    sql: `\n      ALTER TABLE users_test\n      ADD COLUMN status VARCHAR(50) DEFAULT 'active'\n    `,\n    params: [],\n  },\n\n  down: {\n    sql: `\n      ALTER TABLE users_test\n      DROP COLUMN status\n    `,\n    params: [],\n  },\n};\n```\n\nRun up using connection config:\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\n\nasync function main() {\n  const result = await runMigrations({\n    connection: {\n      host: \"localhost\",\n      user: \"root\",\n      password: \"password\",\n      database: \"test_db\",\n      port: 3306,\n    },\n\n    migrationTableName: \"_data_migrations\",\n    migrations: \"./migrations\",\n  });\n\n  console.log(JSON.stringify(result, null, 2));\n}\n\nmain().catch(console.error);\n```\n\nRun up using existing connection:\n\n```js\nconst { runMigrations } = require(\"@atith/mysql-data-migrator\");\nconst mysql = require(\"mysql2/promise\");\n\nasync function main() {\n  const connection = await mysql.createConnection({\n    host: \"localhost\",\n    user: \"root\",\n    password: \"password\",\n    database: \"test_db\",\n    port: 3306,\n  });\n\n  try {\n    const result = await runMigrations({\n      connection,\n      migrationTableName: \"_data_migrations\",\n      migrations: \"./migrations\",\n    });\n\n    console.log(JSON.stringify(result, null, 2));\n  } finally {\n    await connection.end();\n  }\n}\n\nmain().catch(console.error);\n```\n\nRun down:\n\n```js\nconst { rollbackMigrations } = require(\"@atith/mysql-data-migrator\");\n\nasync function main() {\n  const result = await rollbackMigrations({\n    connection: {\n      host: \"localhost\",\n      user: \"root\",\n      password: \"password\",\n      database: \"test_db\",\n      port: 3306,\n    },\n\n    migrationTableName: \"_data_migrations\",\n    migrations: \"./migrations\",\n    steps: 1,\n  });\n\n  console.log(JSON.stringify(result, null, 2));\n}\n\nmain().catch(console.error);\n```\n\n---\n\n## Important Notes\n\n### Data Migrations\n\nThis package is best suited for data migrations such as:\n\n```txt\nUPDATE\nINSERT\nDELETE\nBackfills\nCleanup operations\nData normalization\n```\n\n### Existing Connection Management\n\nWhen you pass a connection config object, the package can create and manage the connection internally.\n\nWhen you pass an existing MySQL connection instance, you are responsible for closing it.\n\n```js\nawait connection.end();\n```\n\nA good pattern is to use `try...finally`:\n\n```js\ntry {\n  await runMigrations({\n    connection,\n    migrations: \"./migrations\",\n  });\n} finally {\n  await connection.end();\n}\n```\n\n### Schema Migrations\n\nYou can run schema migrations such as:\n\n```sql\nALTER TABLE users_test ADD COLUMN status VARCHAR(50)\n```\n\nBut MySQL may auto-commit DDL statements.\n\nThat means rollback may not behave the same way as it does for normal data queries.\n\nUse schema migrations carefully.\n\n### Irreversible Data Migrations\n\nSome data migrations cannot be perfectly reversed.\n\nExample:\n\n```sql\nUPDATE users_test\nSET name = TRIM(name)\n```\n\nBefore:\n\n```txt\n\"  Atith  \"\n```\n\nAfter:\n\n```txt\n\"Atith\"\n```\n\nA `down` query cannot restore the exact original spaces unless you saved a backup.\n\nFor such cases, avoid adding `down`, or set it to `null`.\n\n---\n\n## API\n\n### `runMigrations(config)`\n\nRuns pending `up` migrations.\n\n```js\nawait runMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n});\n```\n\nConfig options:\n\n|         Option       |        Type         | Required |                           Description                                |\n| -------------------- | ------------------- | -------- | -------------------------------------------------------------------- |\n| `connection`         | `object`            | Yes      | MySQL connection config object or existing MySQL connection instance |\n| `migrationTableName` | `string`            | No       | Migration history table name. Defaults to `_data_migrations`         |\n| `migrations`         | `array` or `string` | Yes      | Migration array or migration folder path                             |\n\n### `rollbackMigrations(config)`\n\nRuns `down` migrations.\n\nRollback by steps:\n\n```js\nawait rollbackMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n  steps: 1,\n});\n```\n\nRollback by specific files:\n\n```js\nawait rollbackMigrations({\n  connection,\n  migrationTableName: \"_data_migrations\",\n  migrations: \"./migrations\",\n  rollbackFiles: [\n    \"./migrations/002_update_status_values.migration.js\",\n  ],\n});\n```\n\nConfig options:\n\n|         Option       |         Type        | Required |                           Description                                  |\n| -------------------- | ------------------- | -------- | --------------------------------------------------------------------   |\n| `connection`         | `object`            | Yes      | MySQL connection config object or existing MySQL connection instance   |\n| `migrationTableName` | `string`            | No       | Migration history table name. Defaults to `_data_migrations`           |\n| `migrations`         | `array` or `string` | Yes      | Migration array or migration folder path                               |\n| `steps`              | `number`            | No       | Number of latest applied migrations to rollback                        |\n| `rollbackFiles`      | `array`             | No       | Specific migration file paths to rollback. Takes priority over `steps` |\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}