{"id":16824888,"url":"https://github.com/jaforbes/pgmg","last_synced_at":"2025-06-30T09:37:18.862Z","repository":{"id":76091351,"uuid":"387949692","full_name":"JAForbes/pgmg","owner":"JAForbes","description":"Simple postgres.js migrations","archived":false,"fork":false,"pushed_at":"2024-08-01T04:10:45.000Z","size":137,"stargazers_count":13,"open_issues_count":10,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-04T03:24:42.136Z","etag":null,"topics":["database","database-migrations","js","migration","migrations","node","postgres"],"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/JAForbes.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-07-21T00:45:07.000Z","updated_at":"2024-12-21T15:37:53.000Z","dependencies_parsed_at":"2024-01-26T16:24:21.197Z","dependency_job_id":"c0575a5f-7be3-4e68-85da-a5cda53b4ef7","html_url":"https://github.com/JAForbes/pgmg","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/JAForbes/pgmg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JAForbes%2Fpgmg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JAForbes%2Fpgmg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JAForbes%2Fpgmg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JAForbes%2Fpgmg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JAForbes","download_url":"https://codeload.github.com/JAForbes/pgmg/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JAForbes%2Fpgmg/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262748756,"owners_count":23358264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","database-migrations","js","migration","migrations","node","postgres"],"created_at":"2024-10-13T11:12:20.525Z","updated_at":"2025-06-30T09:37:18.833Z","avatar_url":"https://github.com/JAForbes.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pgmg\n\npgmg = postgres + migrations\n\n\u003e 🔥🔥 Note, there is a major update incoming that is currently on the `next` branch.  We will write a detailed changelog / migration before releasing it.\n\u003e Do not use `next` in production, but feel free to take a look at what we are working on, and try it out on non critical projects.\n\u003e\n\u003e We also no longer recommend using `npx pgmg`, you should instead install a local semver or git tag to insulate yourself from future breaking changes.\n\u003e Note if you have pgmg installed locally, `npx pgmg` will use your local copy, but if not it will fetch the latest tag, which is a little risky\n\u003e for database migrations we think.\n\n## Quick Start\n\n- `mkdir -p migrations`\n- `touch migrations/first-migration.mjs`\n\n```js\n// migrations/first-migration.js\nexport const name = 'First Migration'\nexport const description = `\n    This is where you can describe what your migration does.\n\n    We automatically trim this so don't worry about indentation etc.\n`\n\n// If this fails, any changes will be rolled back\nexport async function transaction(sql){\n\n    await sql`\n        create table example(\n            a int,\n            b int,\n            primary key (a,b)\n        )\n    `\n}\n```\n\n```bash\n# We don't rely on alphabetical order, you just pass in the files\n# you want to migrate.\n$(npm bin)/pgmg \"$DATABASE_URL\" \"migrations/first-migrations.mjs\"\n```\n\n## What\n\n- A forward only, idempotent, postgres migration tool, with minimal noise but also minimal magic\n- OOTB support for postgres.js, we pass in a preconfigured postgres.js instance just point us at migration files\n- A simple migration file format, just export a transaction function a name and a description\n- All metadata stored in 1 simple table in the same database that you are migrating, makes it easy to get fine-grained control\n\n## How\n\nA very simple script, we simply inject a schema (`pgmg`) and table (`migration`) into your target database.  And record whether or not a migration has run for that migration name before.  If so, we skip that file, if not we run it.\n\nIf the migration file runs without error, we insert a migration row based on the exported properties (name, description).  If it fails, we don't.\n\npgmg has no opinion on migration order, but most of the time, there is only new files that aren't recorded, and in that case they can be run in any order.  So you can just pass `migrations/*` to pgmg and it will ignore migrations it hasn't seen before, and run new ones in sequence in glob order.\n\n`pgmg` stores its metadata in the target database (instead of in a different config file or something).  It does this because it makes it easy to escape out of the migration system when you know what you are doing.  E.g. when you want to run all migrations from scratch in development (`delete from pgmg.migration`) or even (`drop schema pgmg`).\n\nIt also makes testing / local development super logical.  When you wipe the DB, the migration tool will also be wiped and pgpg will reapply changes.\n\n\n## API\n\n### CLI\n\n```\nUsage: pgmg [CONNECTION] [OPTIONS] [FILES]\n\n[CONNECTION]\n- Pass a postgres connection string (just like psql)\n- AND/OR Specify host/user etc as env flags (PGHOST, PGUSER, PGPORT)\n\n[FILES]\n\nAny files passed as arguments after the connection string will be imported as JS migration files.\n\n[OPTIONS]\n\nThe only way to specify a connection is via a pg connection URL.\n\n--ssl \n    | --ssl                 Enables ssl\n    | --ssl=prefer          Prefers ssl\n    | --ssl=require         Requires ssl\n    | --ssl=reject          Reject unauthorized connections\n    | --ssl=no-reject       Do not reject unauthorized connections\n    | --ssl=heroku          --no-ssl-reject if the host ends with a .com\n\n```\n\n### Migration File\n\nA migration file can have either an `action` export or a `transaction` export.  An `action` export gets a raw sql instance.  And performs no rollbacks if there is a failure.  That means you need to manually handle your own error and rollback cases.\n\nThe recommended approach is to use `transaction` instead wherever possible which automatically wraps your migration in [`sql.begin`](https://github.com/porsager/postgres#transactions).  This will work great for most migrations, sometimes though you cannot run your migration in a migration and then you'll need to use `action`.\n\nBeyond that, you _must_ export a unique name property, this name is used by pgmg to determine whether or not this migration has run before.  But, it is also good for reference later to see what migrations have run on this db in the past.  Especially when creating curated or conditional migrations.\n\n`description` is an optional export, but a recommended export.  It is rare you need to change the database schema and there isn't some helpful reason you can provide for the change.  A migration is effectively an admission that our first idea of a model was incorrect or incomplete, that is always worthwhile to document.\n\n#### export transaction\n\nPerform your migration within a transaction.\n\n#### export action\n\nPerform your migration with a raw sql instance, no transaction.\n\nThis is necessary for some schema changes, e.g. role changes, or any usage of `concurrently`.\n\n#### export name (required)\n\nThe name of the migration.\n\n#### export description (recommended)\n\nA description of why this migration needs to occur.\n\n## FAQ\n\n### How do I order my migrations?\n\npgmg will apply migrations in the order you pass them to pgmg as arguments.\n\nSo if you choose to number your migrations, a simple glob will order them.\n\n\n```bash\n# Alphabetical ordered files\n$ ls -l migrations\n01-user-permissions.js\n02-full-text-search.js\n03-magic-link.js\n\n# globbing will natively order alphabetically by default\n$ $(npm bin)/pgmg $DATABASE_URL migrations/*.js\n```\n\nYou could also have a simple text file that acts a manifest and expand the file as arguments like so:\n\nImagine we have a `migrations.txt` file:\n\n```txt\nuser-permissions.js\nfull-text-search.js\nmagic-link.js\n```\n\nWe can expand that file as arguments like so:\n\n```bash\n$(npm bin)/pgmg $DATABASE_URL $(cat migrations.txt)\n```\n\nIf you wanted, your manifest could be json, or yaml, or whatever you want, as long as you can extract the filenames and pass them as arguments.\n\n## Roadmap\n\nThere's a few things I would like to add.\n\n- A dry run, that shows what changes will be made without actually applying them.\n- Optional verbose logging\n- Some simple commands for housekeeping:\n    - Show existing migrations\n    - Remove migrations matching filenames\n    - Remove migrations match a naming convention\n- An interactive bootstrap command that creates a new migration file\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaforbes%2Fpgmg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaforbes%2Fpgmg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaforbes%2Fpgmg/lists"}