{"id":20783004,"url":"https://github.com/matschik/sql-mirror","last_synced_at":"2026-04-18T21:36:08.768Z","repository":{"id":97417748,"uuid":"569543027","full_name":"matschik/sql-mirror","owner":"matschik","description":"[WIP]🪞 Opiniated consistent SQL migrations using reusable raw SQL chunks","archived":false,"fork":false,"pushed_at":"2022-12-02T21:05:33.000Z","size":48,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-26T09:57:46.602Z","etag":null,"topics":["migration-tool","postgresql","sql"],"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/matschik.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-23T04:02:51.000Z","updated_at":"2023-12-29T02:21:03.000Z","dependencies_parsed_at":"2023-03-13T16:14:03.428Z","dependency_job_id":null,"html_url":"https://github.com/matschik/sql-mirror","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/matschik/sql-mirror","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matschik%2Fsql-mirror","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matschik%2Fsql-mirror/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matschik%2Fsql-mirror/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matschik%2Fsql-mirror/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matschik","download_url":"https://codeload.github.com/matschik/sql-mirror/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matschik%2Fsql-mirror/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31986323,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"ssl_error","status_checked_at":"2026-04-18T20:23:29.375Z","response_time":103,"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":["migration-tool","postgresql","sql"],"created_at":"2024-11-17T14:16:26.083Z","updated_at":"2026-04-18T21:36:08.759Z","avatar_url":"https://github.com/matschik.png","language":"JavaScript","readme":"# 🪞 sql-mirror\n\n\u003e Opiniated consistent SQL migrations using reusable raw SQL chunks\n\n## ✅ Requirements\n\n- NodeJS\n\n## ⚙️ Features\n\n- Reusable raw SQL chunks\n- Generate up/down raw SQL migrations\n- Migration tool\n- Consistent table id naming \u0026 uuid_v4 value\n- Systematic created_at/updated_at columns\n- SQL chunks are automatically correctly sorted via topological order\n\n## ✨ Usage\n\n### Installation\n\n```sh\npnpm add -D sql-mirror\n```\n\n### Input\n\n```js\nimport { sql, generateSqlFileContent } from \"sql-mirror\";\nimport fs from \"fs\";\n\nconst sqlMirrorConfig = {\n  extensions: [sql.extension[\"uuid-ossp\"]],\n  tables: [\n    {\n      name: \"user\",\n      columns: [\n        sql.column.email(\"email\"),\n        \"password_hash VARCHAR(255) NOT NULL\",\n        \"password_reset_token VARCHAR(255)\",\n        \"password_reset_token_generated_at TIMESTAMP\",\n        \"email_confirmation_sent_at TIMESTAMP\",\n        \"email_confirmed_at TIMESTAMP\",\n      ],\n    },\n    {\n      name: \"user_permission\",\n      columns: [\"name VARCHAR(255) NOT NULL\", \"description TEXT\"],\n    },\n    {\n      name: \"user_role\",\n      columns: [\"name VARCHAR(255) NOT NULL\", \"description TEXT\"],\n    },\n    {\n      name: \"user_role_permission\",\n      references: [\n        {\n          columnName: \"user_role_id\",\n          tableNameRef: \"user_role\",\n        },\n        {\n          columnName: \"user_permission_id\",\n          tableNameRef: \"user_permission\",\n        },\n      ],\n    },\n    {\n      name: \"reward\",\n      columns: [\n        \"title VARCHAR NOT NULL\",\n        \"img_url VARCHAR\",\n        \"description TEXT NOT NULL\",\n        \"points_price INT NOT NULL\",\n      ],\n    },\n    {\n      name: \"reward_user\",\n      references: [\n        {\n          columnName: \"user_id\",\n          tableNameRef: \"user\",\n        },\n        {\n          columnName: \"reward_id\",\n          tableNameRef: \"reward\",\n        },\n      ],\n    },\n  ],\n};\n\nconst newSqlFile = generateSqlFileContent(sqlMirrorConfig);\n\nconst dirPath = `./migrations/${new Date().toISOString()}`;\n\nfs.mkdirSync(dirPath, { recursive: true });\nfs.writeFileSync(`${dirPath}/up.sql`, newSqlFile.up);\nfs.writeFileSync(`${dirPath}/down.sql`, newSqlFile.down);\nfs.writeFileSync(\n  `${dirPath}/sql-mirror-config.json`,\n  JSON.stringify(sqlMirrorConfig, null, 2)\n);\n```\n\n### Output\n\n`migrations/2022-11-23T17:43:34.408Z/up.sql`\n\n```sql\n-- This file was generated via sql-mirror at 2022-11-23T17:43:34.406Z\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n\nCREATE OR REPLACE FUNCTION updated_at_column()\nRETURNS trigger AS\n$BODY$\nBEGIN\nIF (NEW.updated_at IS NULL) THEN\nNEW.updated_at = NOW();\nEND IF;\nRETURN NEW;\nEND;\n$BODY$\nLANGUAGE plpgsql VOLATILE\nCOST 100;\n\nCREATE TABLE \"user\" (\n    \"user_id\" uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,\n    \"email\" VARCHAR(255) UNIQUE NOT NULL,\n    \"password_hash\" VARCHAR(255) NOT NULL,\n    \"password_reset_token\" VARCHAR(255),\n    \"password_reset_token_generated_at\" TIMESTAMP,\n    \"email_confirmation_sent_at\" TIMESTAMP,\n    \"email_confirmed_at\" TIMESTAMP,\n    \"created_at\" TIMESTAMP DEFAULT (now()),\n    \"updated_at\" TIMESTAMP\n);\nCREATE TRIGGER updated_at_on_user BEFORE UPDATE ON \"user\"\nFOR EACH ROW\nEXECUTE PROCEDURE updated_at_column();\n\n\nCREATE TABLE \"reward\" (\n    \"reward_id\" uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,\n    \"title\" VARCHAR NOT NULL,\n    \"img_url\" VARCHAR,\n    \"description\" TEXT NOT NULL,\n    \"points_price\" INT NOT NULL,\n    \"created_at\" TIMESTAMP DEFAULT (now()),\n    \"updated_at\" TIMESTAMP\n);\nCREATE TRIGGER updated_at_on_reward BEFORE UPDATE ON \"reward\"\nFOR EACH ROW\nEXECUTE PROCEDURE updated_at_column();\n\n\nCREATE TABLE \"reward_user\" (\n    \"reward_user_id\" uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,\n    \"user_id\" uuid NOT NULL REFERENCES \"user\"(user_id),\n    \"reward_id\" uuid NOT NULL REFERENCES \"reward\"(reward_id),\n    \"created_at\" TIMESTAMP DEFAULT (now()),\n    \"updated_at\" TIMESTAMP\n);\nCREATE TRIGGER updated_at_on_reward_user BEFORE UPDATE ON \"reward_user\"\nFOR EACH ROW\nEXECUTE PROCEDURE updated_at_column();\n\n\nCREATE TABLE \"user_role\" (\n    \"user_role_id\" uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,\n    \"name\" VARCHAR(255) NOT NULL,\n    \"description\" TEXT,\n    \"created_at\" TIMESTAMP DEFAULT (now()),\n    \"updated_at\" TIMESTAMP\n);\nCREATE TRIGGER updated_at_on_user_role BEFORE UPDATE ON \"user_role\"\nFOR EACH ROW\nEXECUTE PROCEDURE updated_at_column();\n\n\nCREATE TABLE \"user_permission\" (\n    \"user_permission_id\" uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,\n    \"name\" VARCHAR(255) NOT NULL,\n    \"description\" TEXT,\n    \"created_at\" TIMESTAMP DEFAULT (now()),\n    \"updated_at\" TIMESTAMP\n);\nCREATE TRIGGER updated_at_on_user_permission BEFORE UPDATE ON \"user_permission\"\nFOR EACH ROW\nEXECUTE PROCEDURE updated_at_column();\n\n\nCREATE TABLE \"user_role_permission\" (\n    \"user_role_permission_id\" uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,\n    \"user_role_id\" uuid NOT NULL REFERENCES \"user_role\"(user_role_id),\n    \"user_permission_id\" uuid NOT NULL REFERENCES \"user_permission\"(user_permission_id),\n    \"created_at\" TIMESTAMP DEFAULT (now()),\n    \"updated_at\" TIMESTAMP\n);\nCREATE TRIGGER updated_at_on_user_role_permission BEFORE UPDATE ON \"user_role_permission\"\nFOR EACH ROW\nEXECUTE PROCEDURE updated_at_column();\n```\n\n`migrations/2022-11-23T17:43:34.408Z/down.sql`\n\n```sql\n-- This file was generated via sql-mirror at 2022-11-23T17:43:34.406Z\nDROP TRIGGER IF EXISTS \"updated_at_on_user\" ON \"user\";\nDROP TABLE IF EXISTS \"user\";\n\nDROP TRIGGER IF EXISTS \"updated_at_on_reward\" ON \"reward\";\nDROP TABLE IF EXISTS \"reward\";\n\nDROP TRIGGER IF EXISTS \"updated_at_on_reward_user\" ON \"reward_user\";\nDROP TABLE IF EXISTS \"reward_user\";\n\nDROP TRIGGER IF EXISTS \"updated_at_on_user_role\" ON \"user_role\";\nDROP TABLE IF EXISTS \"user_role\";\n\nDROP TRIGGER IF EXISTS \"updated_at_on_user_permission\" ON \"user_permission\";\nDROP TABLE IF EXISTS \"user_permission\";\n\nDROP TRIGGER IF EXISTS \"updated_at_on_user_role_permission\" ON \"user_role_permission\";\nDROP TABLE IF EXISTS \"user_role_permission\";\n\nDROP FUNCTION updated_at_column();\n\nDROP EXTENSION IF EXISTS \"uuid-ossp\";\n```\n\n## 🤔 Why ?\n\nIf you attempt to write raw SQL to make migrations, you will encounter these problems:\n\n- order carefully because of dependent table relations\n- rewrite same function/triggers usage\n- repeat writing created_at/updated_at table columns\n- writing consistent foreign keys is painful\n- ...\n\nI wrote sql-mirror to be as close to raw SQL but with reusable raw SQL chunks\n\n## ⚖️ License\n\nMIT. Made with 💖\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatschik%2Fsql-mirror","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatschik%2Fsql-mirror","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatschik%2Fsql-mirror/lists"}