{"id":48382694,"url":"https://github.com/bug3/sql-render","last_synced_at":"2026-04-16T23:03:13.132Z","repository":{"id":349410270,"uuid":"1202079060","full_name":"bug3/sql-render","owner":"bug3","description":"Type-safe {{variable}} renderer for .sql files","archived":false,"fork":false,"pushed_at":"2026-04-05T20:22:03.000Z","size":319,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-05T21:14:15.932Z","etag":null,"topics":["athena","sql","sql-injection","template-engine","trino","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/sql-render","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bug3.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":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-05T15:12:04.000Z","updated_at":"2026-04-05T20:22:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bug3/sql-render","commit_stats":null,"previous_names":["bug3/sql-render"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/bug3/sql-render","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug3%2Fsql-render","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug3%2Fsql-render/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug3%2Fsql-render/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug3%2Fsql-render/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bug3","download_url":"https://codeload.github.com/bug3/sql-render/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug3%2Fsql-render/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31907728,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"ssl_error","status_checked_at":"2026-04-16T18:21:47.142Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["athena","sql","sql-injection","template-engine","trino","typescript"],"created_at":"2026-04-05T21:13:55.782Z","updated_at":"2026-04-16T23:03:13.122Z","avatar_url":"https://github.com/bug3.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sql-render\n\n[![CI](https://github.com/bug3/sql-render/actions/workflows/ci.yml/badge.svg)](https://github.com/bug3/sql-render/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/sql-render)](https://www.npmjs.com/package/sql-render)\n[![license](https://img.shields.io/npm/l/sql-render)](LICENSE)\n[![node](https://img.shields.io/node/v/sql-render)](package.json)\n\nType-safe `{{variable}}` templating for `.sql` files with built-in injection protection.\n\n- Zero runtime dependencies\n- Built-in SQL injection protection\n- Schema-based validation with type inference\n- Custom schema types for project-specific rules\n- Works with SQL engines that treat backslash as literal in strings (Athena, Trino, PostgreSQL)\n- Compatible with [sql-formatter](https://github.com/sql-formatter-org/sql-formatter)\n\n## Installation\n\n```bash\nnpm install sql-render\n```\n\n## Quick Start\n\nCreate a SQL file with `{{variable}}` placeholders:\n\n```sql\n-- queries/getEvents.sql\nSELECT event_id, event_name\nFROM {{tableName}}\nWHERE status = '{{status}}'\n  AND created_at \u003e= '{{startDate}}'\nORDER BY {{orderBy}}\nLIMIT {{limit}}\n```\n\nDefine and use the query in TypeScript:\n\n```typescript\nimport { defineQuery } from 'sql-render';\n\nconst getEvents = defineQuery\u003c{\n  tableName: string;\n  status: string;\n  startDate: string;\n  orderBy: string;\n  limit: number;\n}\u003e('./queries/getEvents.sql');\n\nconst { sql } = getEvents({\n  tableName: 'prod_events',\n  status: 'active',\n  startDate: '2022-02-22',\n  orderBy: 'created_at',\n  limit: 99,\n});\n```\n\nResult:\n\n```sql\nSELECT\n  event_id,\n  event_name\nFROM\n  prod_events\nWHERE\n  status = 'active'\n  AND created_at \u003e= '2022-02-22'\nORDER BY\n  created_at\nLIMIT\n  99\n```\n\n## Schema Validation\n\nFor stricter validation, define a schema instead of a generic type. Types are inferred automatically.\n\n```typescript\nimport { defineQuery, schema } from 'sql-render';\n\nconst getEvents = defineQuery('./queries/getEvents.sql', {\n  tableName: schema.identifier,\n  status: schema.enum('active', 'pending', 'done'),\n  startDate: schema.isoDate,\n  orderBy: schema.identifier,\n  limit: schema.positiveInt,\n});\n\nconst { sql } = getEvents({\n  tableName: 'prod_events',\n  status: 'active',\n  startDate: '2022-02-22',\n  orderBy: 'created_at',\n  limit: 99,\n});\n```\n\n### Available Schema Types\n\n| Type | Format | Example |\n|------|--------|---------|\n| `schema.string` | Any string (with SQL injection check) | `'hello'` |\n| `schema.number` | Finite number | `33`, `3.14` |\n| `schema.boolean` | `true` / `false` | `true` |\n| `schema.isoDate` | `YYYY-MM-DD` | `'2022-02-22'` |\n| `schema.isoTimestamp` | ISO 8601 with timezone | `'2022-02-22T22:02:22.000Z'` |\n| `schema.identifier` | SQL identifier (up to `db.schema.table`) | `'public.users'` |\n| `schema.uuid` | UUID hex format (8-4-4-4-12) | `'550e8400-e29b-41d4-a716-446655440000'` |\n| `schema.positiveInt` | Positive integer | `100` |\n| `schema.enum(...)` | Whitelist of allowed values | `schema.enum('asc', 'desc')` |\n| `schema.s3Path` | S3 URI | `'s3://athena-results/queries/'` |\n\n### Custom Schema Types\n\nDefine your own type descriptors for project-specific validation:\n\n```typescript\nimport { defineQuery, schema } from 'sql-render';\n\nconst prodTable = {\n  validate: (val: unknown) =\u003e typeof val === 'string' \u0026\u0026 val.startsWith('prod_'),\n};\n\nconst query = defineQuery('./query.sql', {\n  table: prodTable,\n  startDate: schema.isoDate,\n  limit: schema.positiveInt,\n});\n```\n\nA type descriptor is any object with a `validate(val: unknown) =\u003e boolean` method.\n\n## SQL Injection Protection\n\n`schema.string` and the generic `string` type check values against built-in patterns:\n\n| Pattern | Examples |\n|---------|----------|\n| Comments | `--`, `/*`, `*/` |\n| Statement separator | `;` |\n| DDL commands | `DROP`, `ALTER`, `TRUNCATE`, `CREATE` |\n| UNION injection | `UNION SELECT`, `UNION ALL SELECT` |\n| DML commands | `INSERT INTO`, `DELETE FROM`, `UPDATE ... SET` |\n| Execution | `EXEC`, `EXECUTE` |\n| Time-based | `SLEEP()`, `BENCHMARK()`, `WAITFOR DELAY` |\n| System procedures | `xp_*`, `sp_*` |\n| Privilege commands | `GRANT`, `REVOKE` |\n| File operations | `LOAD_FILE()`, `INTO OUTFILE`, `INTO DUMPFILE` |\n| Data loading | `LOAD DATA` |\n\nPatterns use word boundaries to avoid false positives (e.g., \"backdrop\" won't trigger `DROP`).\n\nOther schema types like `schema.identifier`, `schema.isoDate`, `schema.uuid` etc. are inherently safe due to their strict format validation.\n\nThe pattern list is exported for reference:\n\n```typescript\nimport { SQL_INJECTION_PATTERNS } from 'sql-render';\n```\n\n## Error Messages\n\n| Scenario | Error |\n|----------|-------|\n| File not found | `File not found: ./query.sql` |\n| Schema mismatch | `Schema missing definitions for template variables: [id]` |\n| Missing params | `Missing variables in params: [tableName, limit]` |\n| Extra params | `Extra variables not in template: [foo]` |\n| Schema validation | `Schema validation failed for 'status': received string (\"invalid\")` |\n| Type validation | `SQL injection pattern detected in 'status': ...` |\n| Null/undefined | `Validation failed for 'key': value cannot be null or undefined` |\n| Invalid descriptor | `Invalid schema descriptor for 'key': must have a validate(val) method` |\n\n## Security Model\n\nsql-render protects against SQL injection using a **denylist + escape** strategy, not parameterized queries (prepared statements). Values are validated and escaped before being interpolated directly into the SQL string.\n\nThis is effective for engines that don't support parameterized queries (e.g., Athena, Trino DDL, ad-hoc SQL scripts). If your database driver supports parameterized queries, prefer using them as the primary defense and treat sql-render's protection as an additional layer.\n\nThe built-in denylist does not guarantee 100% protection against all SQL injection vectors. For stricter control, define [custom schema types](#custom-schema-types) tailored to your project's specific validation needs.\n\nTo report a vulnerability, see [SECURITY.md](SECURITY.md).\n\n## sql-formatter Compatibility\n\nThe `{{variable}}` syntax is fully compatible with [sql-formatter](https://github.com/sql-formatter-org/sql-formatter). A `paramTypes` custom regex is required so that `{{variables}}` containing SQL keywords (e.g. `{{limit}}`) are treated as parameters instead of being parsed as SQL.\n\n### VS Code\n\nInstall the [SQL Formatter VSCode](https://marketplace.visualstudio.com/items?itemName=ReneSaarsoo.sql-formatter-vsc) extension, then copy [`.vscode/settings.json`](.vscode/settings.json) into your project to enable format-on-save with the recommended settings:\n\n```json\n{\n    \"[sql]\": {\n        \"editor.defaultFormatter\": \"ReneSaarsoo.sql-formatter-vsc\",\n        \"editor.formatOnSave\": true\n    },\n    \"SQL-Formatter-VSCode.dialect\": \"trino\",\n    \"SQL-Formatter-VSCode.keywordCase\": \"upper\",\n    \"SQL-Formatter-VSCode.functionCase\": \"upper\",\n    \"SQL-Formatter-VSCode.dataTypeCase\": \"upper\",\n    \"SQL-Formatter-VSCode.paramTypes\": {\n        \"custom\": [{ \"regex\": \"\\\\{\\\\{[a-zA-Z_][a-zA-Z0-9_]*\\\\}\\\\}\" }]\n    }\n}\n```\n\n## For LLMs\n\nTwo machine-readable summaries are maintained for AI consumption, following the [llms.txt](https://llmstxt.org) convention:\n\n- [llms.txt](llms.txt) - curated API reference and usage guide\n- [llms-full.txt](llms-full.txt) - full packed source, auto-generated on each push\n\n## License\n\n[MIT](https://github.com/bug3/sql-render/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbug3%2Fsql-render","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbug3%2Fsql-render","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbug3%2Fsql-render/lists"}