{"id":15365950,"url":"https://github.com/andywer/postguard","last_synced_at":"2025-03-17T11:09:38.566Z","repository":{"id":34080596,"uuid":"162233745","full_name":"andywer/postguard","owner":"andywer","description":"🐛 Statically validate Postgres SQL queries in JS / TS code and derive schemas.","archived":false,"fork":false,"pushed_at":"2023-02-28T05:50:03.000Z","size":2273,"stargazers_count":165,"open_issues_count":16,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-05-02T06:16:41.837Z","etag":null,"topics":["babel","database","database-schema","nodejs","postgresql","query-builder","schema","sql","sql-query","typescript","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andywer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-12-18T05:15:32.000Z","updated_at":"2024-03-20T00:13:47.000Z","dependencies_parsed_at":"2023-12-19T01:01:26.098Z","dependency_job_id":"f34814db-3743-4a0c-9a74-b989adf30a84","html_url":"https://github.com/andywer/postguard","commit_stats":{"total_commits":68,"total_committers":3,"mean_commits":"22.666666666666668","dds":"0.20588235294117652","last_synced_commit":"e9f6ed36a76c9a706f18b997a9044426831706e5"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fpostguard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fpostguard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fpostguard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fpostguard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andywer","download_url":"https://codeload.github.com/andywer/postguard/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244022645,"owners_count":20385135,"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":["babel","database","database-schema","nodejs","postgresql","query-builder","schema","sql","sql-query","typescript","validation"],"created_at":"2024-10-01T13:16:47.351Z","updated_at":"2025-03-17T11:09:38.541Z","avatar_url":"https://github.com/andywer.png","language":"TypeScript","readme":"\u003ch1 align=\"center\"\u003epostguard\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eValidate SQL queries in JavaScript and TypeScript code against your schema at build time 🚀\u003c/b\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://travis-ci.org/andywer/postguard\"\u003e\u003cimg alt=\"Build status\" src=\"https://travis-ci.org/andywer/postguard.svg?branch=master\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/postguard\"\u003e\u003cimg alt=\"npm version\" src=\"https://img.shields.io/npm/v/postguard.svg\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\nLocates SQL template strings and schema definitions in your code. Evaluates the queries, matching them against your database schema. Supports type-checking via TypeScript, so you get **statically typed SQL queries validated against your database schema** 😱😱\n\nUse with [squid](https://github.com/andywer/squid). It provides SQL tagged template strings, auto-escapes dynamic expressions to prevent SQL injections and comes with some syntactic sugar to write short, explicit SQL queries.\n\n🦄\u0026nbsp;\u0026nbsp;Validates SQL template strings in code\u003cbr /\u003e\n🚀\u0026nbsp;\u0026nbsp;Checks SQL queries [syntax and semantics](#validations)\u003cbr /\u003e\n⚡️\u0026nbsp;\u0026nbsp;Works statically, without additional runtime overhead\u003cbr /\u003e\n⚙️\u0026nbsp;\u0026nbsp;Built on top of Babel \u0026 TypeScript\u003cbr /\u003e\n🛠\u0026nbsp;\u0026nbsp;Uses `libpg_query`, the actual Postgres SQL parser\u003cbr /\u003e\n\n---\n\n\u003cbr /\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Screencast\" src=\"./docs/screencast.gif\" width=\"80%\" /\u003e\n\u003c/p\u003e\n\n## Installation\n\n```sh\nnpm install --save-dev postguard\n\n# or using yarn:\nyarn add --dev postguard\n```\n\n## CLI\n\nRun the tool like this:\n\n```sh\npostguard src/models/*\n```\n\nWe can use npm's [npx tool](https://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner) to run the locally installed package:\n\n```sh\nnpx postguard src/models/*\n```\n\n## Command line options\n\n```\nUsage\n  $ postguard ./path/to/source/*.ts\n\nOptions\n  --help        Print this help\n  -w, --watch   Watch files and re-evaluate on change\n```\n\n## Guide\n\n- **[Usage](./docs/usage.md)** - Hands-on examples how to use the tool\n- **[Validations](./docs/validations.md)** - List of validations that will be performed\n\n## Motivation\n\nLet's quickly compare the options you got when writing code that uses a relational database.\n\nOur sample use case is updating project rows that are owned by a certain user.\n\n### Plain SQL\n\nSample:\n\n\u003c!-- prettier-ignore-start --\u003e\n```js\nconst { rows } = await database.query(`\n  UPDATE projects SET\n    last_opened = NOW(),\n    open_count = open_count + 1\n  WHERE\n    projects.id IN (\n      SELECT project_id FROM project_members WHERE user_id = $1\n    )\n  RETURNING *\n`,\n  [ userId ]\n)\n```\n\u003c!-- prettier-ignore-end --\u003e\n\nPro:\n\n- Efficient queries\n- Explicit - No magic, full control\n- Functional stateless data flow, atomic updates\n\nCon:\n\n- Very easy to make mistakes\n- No way of telling if correct unless code is run\n- Can be quite verbose\n- Requires knowledge about SQL \u0026 your database\n- No type safety\n\n### ORMs (Sequelize, TypeORM, ...)\n\nSample:\n\n```js\n// (Model definitions not included)\n\nconst user = await User.findById(userId)\nconst projects = await user.getProjects()\n\nconst updatedProjects = await Promise.all(\n  projects.map(async project =\u003e {\n    project.last_opened = new Date(Date.now())\n    project.open_count++\n    return project.save()\n  })\n)\n```\n\nPro:\n\n- Easy to get started\n- Type-safety\n- Less error-prone than writing raw SQL\n- Requires no SQL knowledge\n\nCon:\n\n- Implicit - Actual database queries barely visible\n- Usually leads to inefficient queries\n- Update operations based on potentially stale local data\n- Virtually limits you to a primitive subset of your database's features\n\n### Query builder (Knex.js, Prisma, ...)\n\nSample:\n\n```js\n// (Model definitions not included)\n\nconst usersProjects = await prisma.user({ id: userId }).projects()\n\nconst updatedProjects = await Promise.all(\n  projects.map(project =\u003e\n    prisma.updateProject({\n      data: {\n        last_opened: new Date(Date.now()),\n        open_count: project.open_count + 1\n      },\n      where: {\n        id: project.id\n      }\n    })\n  )\n)\n```\n\nPro:\n\n- Explicit - Full control over queries\n- Functional stateless data flow\n- Type-safety\n\nCon:\n\n- Additional abstraction layer with its own API\n- Atomic updates still hardly possible\n- Requires knowledge about both, SQL \u0026 your database plus the query builder API\n\n### SQL with squid \u0026 postguard 🚀\n\nSample:\n\n```ts\n// (Schema definition not included)\n\nconst { rows } = await database.query\u003cProjectRecord\u003e(sql`\n  UPDATE projects SET\n    last_opened = NOW(),\n    open_count = open_count + 1\n  WHERE\n    projects.id IN (\n      SELECT project_id FROM project_members WHERE user_id = ${userId}\n    )\n  RETURNING *\n`)\n```\n\nPro:\n\n- Explicit - Full control, no implicit magic\n- Fast due to absence of abstraction layers\n- Functional stateless data flow, atomic updates\n- Full query validation at build time\n- Type-safety\n\nCon:\n\n- Requires knowledge about SQL \u0026 your database\n\n## Debugging\n\nSet the environment variable `DEBUG` to `postguard:*` to enable debug logging. You can also narrow debug logging down by setting `DEBUG` to `postguard:table` or `postguard:query`, for instance.\n\n## Questions? Feedback?\n\nFeedback is welcome, as always. Feel free to comment what's on your mind 👉 [here](https://github.com/andywer/postguard/issues/1).\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandywer%2Fpostguard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandywer%2Fpostguard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandywer%2Fpostguard/lists"}