{"id":21934290,"url":"https://github.com/beardyman/forge","last_synced_at":"2026-05-01T00:31:27.454Z","repository":{"id":40482269,"uuid":"438798289","full_name":"beardyman/forge","owner":"beardyman","description":"Migrations for any system","archived":false,"fork":false,"pushed_at":"2023-02-12T14:39:32.000Z","size":1338,"stargazers_count":1,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-22T13:35:58.999Z","etag":null,"topics":["database","migration"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beardyman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-15T23:06:46.000Z","updated_at":"2022-02-16T02:14:27.000Z","dependencies_parsed_at":"2023-02-18T14:45:55.941Z","dependency_job_id":null,"html_url":"https://github.com/beardyman/forge","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/beardyman/forge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beardyman%2Fforge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beardyman%2Fforge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beardyman%2Fforge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beardyman%2Fforge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beardyman","download_url":"https://codeload.github.com/beardyman/forge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beardyman%2Fforge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32481553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","migration"],"created_at":"2024-11-29T00:15:15.146Z","updated_at":"2026-05-01T00:31:27.429Z","avatar_url":"https://github.com/beardyman.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Forge\nA utility for deploying migrations for changing system state. \n---\n\n![Unit Tests](https://github.com/beardyman/forge/actions/workflows/unit-tests.yml/badge.svg)\n![Functional Tests](https://github.com/beardyman/forge/actions/workflows/func-tests.yml/badge.svg)\n\n\nMigrations are generally database schema alterations or static data updates but can be anything that \nis able to be executed via javascript. The common use case (and the reason this lib was written) is for database\nschema migrations, however at its core this library just keeps track of state for files that are run, so it can be \nused to migrate the state of anything. File state, database state, infrastructure state; it all depends on the \nmigrations that you create.\n\n## Getting Started\n1. Create a plugin\n2. Create migrations\n3. Configure `forge` to point to that plugin and migrations\n4. Run `forge migrate`\n\n## Why?\nSo many alternative migration tools (flyway, knex.js) are written with specific database support and they \nare generally bloated supporting many features that many teams don't need. For teams that use multiple \ndatabase systems it's sometimes hard to find a single migration tool that supports all of their databases which\nleads to using multiple, generally very different, tools.\n\nI wanted to create a tool where users could easily write their own plugin for state management without much effort, \nallowing them to setup database connections how they see fit. The user can easily write the logic to create \nschemas, tables and inserting state data for this tool so it can easily support any database or system that the user\nwants to use.  Technically, a user could use a file or store the state in s3 if they wanted to.\n\n### Why are schema migrations important?\n\"Ok, cool, but why would I want to use this tool?\".  I hear it, I get what you're saying. If it just runs js files that\nrun SQL (or whatever), why can't I do that manually?  Short story is, you can. However, the point of this tool is \nto make it easy to manage state across multiple environments for repeatable deployments. This makes it easy to \ndeploy your schema with your code.  You can run `forge` with your deployment to deploy database changes alongside code.\nThis allows you to easily rollout or rollback database changes to match the state of your deployed code.\n\n## How do I use it?\n### Setup a Migration State Plugin\nThis is easier than it sounds. Forge requires a plugin to manage its own state to know which files have been run.\nThis creates an interface for `forge` to be able to create and manage tables for managing its own state.\n\n#### Migration State Plugin requirements\nForge was written using ESM and requires that its plugins do as well. The plugin must export a class by default and \nmust implement the following 5 methods:\n* `createSchema(schema)`\n  * Creates the schema where the state table resides.  Should be able to be ran repeatedly.\n  * `schema` is a string with the name of the schema.  This is comes from the config (see below).\n* `createTable(tableName, tableColumnMap)`\n  * Creates a table to store the state.\n  * `tableName` is a string with the name of the table from the config.\n  * `tableColumnMap` is an object mapping column names to data types.  All types passed are of type 'text', this may need to \n    be translated to an acceptable type for your system.\n* `insert(columnValueMap)`\n  * Inserts a record into the state table during a migration. \n  * `columnValueMap` is a mapping of column names to values to be inserted.\n* `remove(version)`\n  * Removes a record from the state table during a rollback.\n  * `version` is the version be removed.\n* `getMigrationState()`\n  * Returns all records from the state table.  Expected format is an array of objects: \n  ```json \n  [{ \"version\": \"1\", \"name\": \"a migration\", \"filename\": \"v1_a_migration.js\"}]\n  ```\n\nAn interface can be extended if `forge` is imported into the plugin file.  This will ensure appropriate methods are implemented.\n```js\nimport PluginInterface from 'forge';\n\nexport default class MyStatePlugin extends PluginInterface {\n  //...\n}\n```\n\nIf using a SQL backend, there is also a General SQL interface that includes all of the necessary functions implemented.  It uses PosgreSQL syntax but \nshould work for most SQL databases.  It requires that a constructor be made assigning a database interface to `this.db`.  The database interface must \nhave a function named `query`. An example of its implementation creating a connection to a local postgres instance:\n```js\nimport { GeneralSQLInterface } from 'forge';\n\nimport pgp from 'pg-promise';\nconst pgDb = pgp();\nexport const pg = pgDb({database: 'postgres'});\n\nexport default class Postgres extends Sql {\n  constructor(config) {\n    super(config);\n    this.db = pg; // pg.query will execute queries\n  }\n}\n```\n\n### Create migrations\nMigrations must all be placed in a the `migrationsDirectory` from configuration. It must follow the format: `v\u003cversion\u003e_\u003cname\u003e.js`\nFor example, the following will result in a migration with `version: 1` and `name: my migration file`:\n```\nv1_my_migration_file.js\n```\nThe version can be any string or number as long as it fits in sort order.\n\nA migration must export two methods: `migrate` and `rollback`.  The full forge config will be passed to each method.  Each method must return a promise.  \nAn example migration:\n```js\nimport {pg} from './postgres.js'; // wrapper around pg-promise\n\nexport const migrate = (config) =\u003e {\n  const query = `\n  CREATE TABLE IF NOT EXISTS mySchema.accounts (\n    id                int primary key,\n    account_settings  jsonb\n  );`;\n  return pg.query(query);\n};\n\nexport const rollback = (config) =\u003e {\n  const query = `\n  DROP TABLE IF EXISTS mySchema.accounts;\n  `;\n  return pg.query(query);\n};\n```\n\n### Configuration\nForge's configuration should be set in the project's `package.json` file under the property `\"forge\"`. The configuration can be in two different formats.\nThe default format can be used if there's only one set of migrations in the project.\n\nThe only **required** property of a configuration is `\"migrationStatePlugin\"`.\n\nConfig defaults:\n```json \n{\n  \"logLevel\": \"info\",\n  \"migrationsDirectory\": \"migrations\",\n  \"migrationTable\": \"forge_migrations\",\n  \"schema\": \"public\"\n}\n```\n\nExample configuration: \n```json\n{\n  \"forge\": {\n    \"migrationStatePlugin\": \"./schema/postgres.js\",\n    \"migrationsDirectory\": \"./schema\",\n    \"schema\": \"test_migrations\",\n    \"logLevel\": \"debug\"\n  }\n}\n```\n\nThe other configuration format is for named configurations.  This can be useful if your project requires two separate migrations.  Names are arbitrary \nand there are no limits to how many named migrations you can have.  The defaults above apply to each named configuration.\n```json\n{\n  \"forge\": {\n    \"logLevel\": \"debug\",\n    \"db\": {\n      \"migrationStatePlugin\": \"./schema/postgres.js\",\n      \"migrationsDirectory\": \"./schema\",\n      \"schema\": \"test_migrations\"\n    },\n    \"infra\": {\n      \"migrationStatePlugin\": \"./infra/aws.js\",\n      \"migrationsDirectory\": \"./infra\",\n      \"schema\": \"infra_migrations\"\n    }\n  }\n}\n```\n\n### Run migrate or rollback\n```shell\n# record existing migrations\nforge initialize\n\n# record existing migrations prior to and including a specific version\nforge initialize --version \u003cversion\u003e\n\n# migrate to latest\nforge migrate\n\n# migrate using a named configuration\nforge migrate \u003cname\u003e\n\n# migrate to a specific version\nforge migrate --version \u003cversion\u003e\n\n# rollback to the previous version\nforge rollback\n\n# rollback using a named configuration\nforge rollback \u003cname\u003e\n\n# rollback to a specific version (sets the state to the version specified)\nforge rollback --version \u003cversion\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeardyman%2Fforge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeardyman%2Fforge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeardyman%2Fforge/lists"}