{"id":19974559,"url":"https://github.com/twooster/sqigil","last_synced_at":"2025-09-13T15:43:39.621Z","repository":{"id":34300132,"uuid":"175945546","full_name":"twooster/sqigil","owner":"twooster","description":"SQigiL: A Postgres SQL template string for Javascript","archived":false,"fork":false,"pushed_at":"2023-01-07T04:05:10.000Z","size":671,"stargazers_count":11,"open_issues_count":6,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-12T14:56:36.059Z","etag":null,"topics":["escape","formatting","javascript","postgres","sql","typescript"],"latest_commit_sha":null,"homepage":"","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/twooster.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-16T08:21:54.000Z","updated_at":"2022-01-21T04:23:11.000Z","dependencies_parsed_at":"2023-01-15T06:05:19.527Z","dependency_job_id":null,"html_url":"https://github.com/twooster/sqigil","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twooster%2Fsqigil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twooster%2Fsqigil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twooster%2Fsqigil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twooster%2Fsqigil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twooster","download_url":"https://codeload.github.com/twooster/sqigil/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224379812,"owners_count":17301525,"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":["escape","formatting","javascript","postgres","sql","typescript"],"created_at":"2024-11-13T03:15:24.501Z","updated_at":"2024-11-13T03:15:25.409Z","avatar_url":"https://github.com/twooster.png","language":"TypeScript","readme":"# [SQigiL](https://github.com/twooster/sqigil): A Postgres SQL template string for Javascript\n\n[![CircleCI](https://img.shields.io/circleci/project/github/twooster/sqigil/master.svg)](https://circleci.com/gh/twooster/sqigil)\n[![Coverage Status](https://img.shields.io/coveralls/github/twooster/sqigil.svg)](https://coveralls.io/github/twooster/sqigil)\n\nThis project provides an easy-to-use, safe, SQL string templating\nsolution. It's built to work with\n[ES2015 template strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals).\n\nThis project is built in Typescript, and will maintain 100% test\ncoverage.\n\nDisclaimer: This project is still pretty new. I'm fairly confident in\nits safety, but for now, you're a beta user. Please feel free to\n[create an issue](https://github.com/twooster/sqigil/issues) (or a pull\nrequest) if you find any bugs.\n\n## Installation\n\n```sh\nnpm install --save sqigil\n```\n\n## Documentation\n\n**Documentation is available [here](https://twooster.github.io/sqigil)**\n\nDocumentation is updated every version bump. A changelog is available\n[here](https://github.com/twooster/sqigil/blob/master/CHANGELOG.md).\n\n## Motivation\n\nProjects such as [pg-promise](https://github.com/vitaly-t/pg-promise) provide\nSQL templating functionality, but incur overhead actually performing\nstring parsing. With the advent of ES2015, it's simple to provide safe\nand fast string templating. This project is an attempt to implement such\na solution.\n\n## Usage\n\n### Quick Reference\n\nThe standard form for producing SQL strings is:\n\n```javascript\nimport { sql } from 'sqigil'\n\n// Bare value inclusion:\n\nsql`SELECT * FROM users WHERE name = ${\"O'Connor\"}`\n// Or:\nsql`SELECT * FROM users WHERE name = ${sql.value(\"O'Connor\")}`\n// \"SELECT * FROM users WHERE name = 'O''Connor'\"\n\n// Explicit conversion:\n\nsql`SELECT * FROM users WHERE id IN (${sql.csv([1, 2, 3, 4])})`\n// \"SELECT * FROM users WHERE id IN (1, 2, 3, 4)\"\n\nsql`SELECT * FROM users WHERE active = ${sql.bool('yes')}`\n// \"SELECT * FROM users WHERE active = TRUE`\n\nconst user = { name: \"John\", active: false }\nsql`INSERT INTO users(${sql.keys(user)}) VALUES (${sql.values(user)})`\n// `INSERT INTO users(\"name\", \"active\") VALUES ('John', FALSE)`\n\nsql`SELECT * FROM ${sql.id('users')}`\n// `SELECT * FROM \"users\"`\n\nsql`SELECT ${sql.csids(['name', 'active'])} FROM users`\n// `SELECT \"name\", \"active\" FROM users`\n\nsql`SELECT * FROM (${sql.raw('SELECT * FROM users')})`\n// `SELECT * FROM (SELECT * FROM users)`\n```\n\n### Conversion Operators\n\n#### `sql.value` or just `\u003cplain value\u003e`\n\nAn escaped Postgres value, dependent on input type.\n\n| Data Type | Conversion | JS Input | SQL Output |\n|--|--|--|--|\n| string | SQL string literal | `\"It's nice!\"` | `'It''s nice!'` |\n| boolean | SQL boolean literal | `true` | `TRUE` |\n| null | SQL NULL | `null` | `NULL` |\n| undefined | SQL NULL | `undefined` | `NULL` |\n| number | SQL number literal (escaped in some instances) | `10`, `1.2`, `Infinity`, `NaN` | `10`, `1.2`, `'+Infinity'`, `'NaN'` |\n| array | Postgres string array literal format, each element escaped | `[1, '\"O\\'Connor\"', [true, false]]` | `'{1, \"\\\"O''Connor\\\"\", {TRUE, FALSE}}'` |\n| Date | SQL date in UTC | `new Date()` | `'2019-03-18T08:11:50.221+00:00'` |\n| Buffer | Hex-encoded Postgres escape-string | `Buffer.from('abc')` | `E'\\x616263'` |\n| object | JSON-encoded SQL string | `{ a: \"Doc'\", b: \"2\" }` | `'{\"a\":\"Doc''\",\"b\":\"2\"'}` |\n| Symbol | error | `Symbol('sym')` | Throws an error |\n| Function | error | `() =\u003e 'Anything'` | Throws an error |\n\n\n#### `sql.bool`\n\n**Outputs:** Converts input value to a SQL boolean based upon Javascript\ntruthiness rules\n\n```javascript\nsql`SELECT ${sql.bool(null)}, ${sql.bool('')}, ${sql.bool('bob')}`\n// SELECT FALSE, FALSE, TRUE\n```\n\n#### `sql.utc`, `sql.tz`\n\n**Outputs:** A SQL date literal. Converts the provided date (must be a date) in\nthe timezone of the local machine (`sql.tz`) or in UTC (`sql.utc`):\n\n```javascript\nconst date = new Date()\nsql`SELECT ${sql.tz(date)}, ${sql.utc(date)}`\n// SELECT '2019-03-18T08:11:50.221+02:00', '2019-03-18T08:09:50.221+00:00'\n```\n\n#### `sql.csv`\n\n**Outputs:** Comma-separated SQL values, each escaped according to its type\n(see `sql.value`):\n\n```javascript\nconst userIds = [1, 2, 3]\nsql`SELECT * FROM users WHERE id IN (${sql.csv(userIds)})`\n// SELECT * FROM users WHERE id IN (1, 2, 3)\n```\n\n#### `sql.csids`\n\n**Outputs:** Comma-separated list, with each value escaped as though it is a\nSQL identifier:\n\n```javascript\nconst cols = ['name', 'join_date']\nsql`SELECT ${sql.csids(cols)} FROM users`\n// SELECT \"name\", \"join_date\" FROM users`\n```\n\n#### `sql.id`\n\n**Outputs:** A single SQL identifier name.  Also accepts arrays for\ndot-separated names:\n\n```javascript\nconst col = 'name'\nconst otherCol = ['interests', 'description']\n\nsql`SELECT ${sql.id(col)}, ${sql.id(otherCol)} FROM users, interests`\n// SELECT \"name\", \"interests\".\"description\" FROM users, interests\n```\n\n#### `sql.keys`, `sql.values`\n\n**Outputs:**\n\nA comma separated list of SQL identifiers (for `sql.keys`) or escaped values\n(for `sql.values`), from the provided object:\n\n```javascript\nconst user = { name: \"John\" }\n\nsql`INSERT INTO users(${sql.keys(user)}) VALUES (${sql.values(user)})`\n// INSERT INTO users(\"name\") VALUES ('John')\n```\n\n#### `sql.raw`\n\nThe provided string (must be a string) with no escaping:\n\n```javascript\nconst subQuery = `SELECT * FROM bands WHERE genre = \"punk\"`\nsql`WITH punk_bands AS (${sql.raw(subQuery)}) SELECT * FROM punk_bands WHERE country2 = ${\"US\"}`\n// WITH punk_bands AS (SELECT * FROM bands WHERE genre = \"punk\") SELECT * FROM punk_bands WHERE country2 = 'US'\n```\n\n### Special Object Conversions\n\n\n`sqigil` also supports the `pg-promise`-convention of special object conversion\nusing `toPostgres`/`rawType` attributes. Both the symbol form\n(`Symbol.for('ctf.toPostgres')`, etc) and the string-attribute form are\nsupported.\n\nExample:\n\n```javascript\nimport { sql, toPostgres, rawType } from 'sqigil'\n\n// toPostgres and rawType are symbols, so won't show up in property enumeration\n\nclass Person {\n  constructor(firstName, lastName) {\n    this.firstName = firstName\n    this.lastName = lastName\n  }\n\n  toPostgres() {\n    // Because `rawType` is not set, this string will be interpreted\n    // as a string by `sql`, and properly escaped\n    return `${this.lastName}, ${this.firstName}`\n  }\n\n  // Or using the symbol, (takes precedence):\n  [toPostgres]() {\n    return `${this.lastName}, ${this.firstName}`\n  }\n}\n\nsql`INSERT INTO people(name) VALUES(${new Person(\"John\", \"O'Connor\")})`\n// INSERT INTO people(name) VALUES('O''Connor, John')\n\nclass HstoreMap {\n  constructor() {\n    this.map = new Map()\n  }\n\n  set(k, v) { this.map.set(k, v) }\n  get(k) { return this.map.get(k) }\n\n  [rawType] = true\n  [toPostgres]() {\n    const kvs = Array.from(this.map.entries())\n    // Instead of setting [rawType] to true, it's also possible\n    // to `return sql.raw('hstore(...)')`\n    return `hstore(ARRAY[${\n      sql.csv(kvs.map(kv =\u003e sql.csv(kv)))\n    }])`;\n  }\n}\n\nconst m = new HstoreMap()\nm.set('a', '1')\nm.set('b', '2')\nsql`INSERT INTO hstore_tbl(attrs) VALUES (${m})`\n// INSERT INTO hstore_tbl(attrs) VALUES (hstore(ARRAY['a', '1', 'b', '2']))\n```\n\nImportant notes:\n* Use only symbol form or string form in a single object. Mixing the two will\n  not work.\n\n* If `rawType` is used, `toPostgres` **must** return a string, or an\n  error will be thrown.\n\n(This feature is built to roughly concur with the interface defined by\n`pg-promise`.)\n\n#### Without The `sql` Tag\n\nNote that it's also possible to template into bare strings without the `sql`\nleader, though it's more dangerous because you must remember to escape every\nvalue:\n\n```javascript\n`INSERT INTO words (word) VALUES (${sql.value(\"John O'Connor\")})`\n// INSERT INTO words(word) VALUES ('John O''Connor')\n\n// Exactly the same as:\nsql`INSERT INTO words (word) VALUES (${\"John O'Connor\"})`\n```\n\nUsing the `sql` leader allows safe bare value inclusion.\n\n## Custom Templating\n\nYou can customize the templater in two ways:\n\n* Custom date formatting (defaults to UTC)\n* Custom object formatting (defaults to `JSON.stringify`)\n\nHow? Simple:\n\n```javascript\nconst { sql, makeSigil, makeSafeString } = require('sqigil')\n\nconst customSql = makeSigil({\n  // Will not be escaped\n  convertDate: (date) =\u003e makeSafeString('DATE!'),\n  convertObject(obj) {\n    if (obj instanceof MySpecialObj) {\n      // Will be escaped as a string\n      return JSON.stringify({\n        theName: obj.getName()\n      })\n    } else {\n      // Will be escaped as a string\n      return JSON.stringify(obj)\n    }\n  }\n})\n\ncustomSql`${new MySpecialObj({ name: \"John\" })}`\n// `'{\"theName\":\"John\"}'`\n\ncustomSql`${new Date()}`\n// `DATE!`\n```\n\n## More Detailed Documentation\n\nSee the [documentation](https://tonywooster.com/sqigil) for a full list\nof available formatting options. Of primary interest are the conversion\nmethods listed in [SqlSigil](https://tonywooster.com/sqigil/interfaces/sqlsigil.html).\n\n## License\n\nMIT, available [here](https://github.com/twooster/sqigil/blob/master/LICENSE).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwooster%2Fsqigil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwooster%2Fsqigil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwooster%2Fsqigil/lists"}