{"id":18322212,"url":"https://github.com/kikuchan/query-weaver","last_synced_at":"2026-04-02T03:50:50.100Z","repository":{"id":65957303,"uuid":"602475866","full_name":"kikuchan/query-weaver","owner":"kikuchan","description":"Query Weaver - SQL query builder using template string literal","archived":false,"fork":false,"pushed_at":"2026-03-15T07:39:58.000Z","size":548,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-15T19:29:14.569Z","etag":null,"topics":["builder","helper","literal","node","postgresql","query","sql","string","tagged","template"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/kikuchan.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":"2023-02-16T09:41:18.000Z","updated_at":"2026-03-15T07:37:39.000Z","dependencies_parsed_at":"2024-01-16T02:02:49.969Z","dependency_job_id":"2830c219-c40e-4a79-8742-815dc17264a0","html_url":"https://github.com/kikuchan/query-weaver","commit_stats":{"total_commits":56,"total_committers":2,"mean_commits":28.0,"dds":0.1607142857142857,"last_synced_commit":"e4b0f18bc749ace33982d3e546b7249557a8a3e2"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/kikuchan/query-weaver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kikuchan%2Fquery-weaver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kikuchan%2Fquery-weaver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kikuchan%2Fquery-weaver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kikuchan%2Fquery-weaver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kikuchan","download_url":"https://codeload.github.com/kikuchan/query-weaver/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kikuchan%2Fquery-weaver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31294527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:43:37.129Z","status":"online","status_checked_at":"2026-04-02T02:00:08.535Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["builder","helper","literal","node","postgresql","query","sql","string","tagged","template"],"created_at":"2024-11-05T18:23:47.343Z","updated_at":"2026-04-02T03:50:50.091Z","avatar_url":"https://github.com/kikuchan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Query Weaver\n\nCompose SQL statements safely by leveraging template string literals.\n\n## Install\n\n```sh\n$ npm install query-weaver\n```\n\n## Basic Usage\n\n### As an SQL Builder\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport { sql } from 'query-weaver';\n\nconst foo = 1, bar = 'Bar';\nconst query = sql`SELECT * FROM foobar WHERE foo = ${foo} AND bar = ${bar}`;\nconsole.log(query);\n// QueryFragments { text: [Getter], values: [Getter], embed: [Getter] }\n\nconsole.log(JSON.stringify(query, null, 2));\n// {\n//   \"text\": \"SELECT * FROM foobar WHERE foo = $1 AND bar = $2\",\n//   \"values\": [\n//     1,\n//     \"Bar\"\n//   ],\n//   \"embed\": \"SELECT * FROM foobar WHERE foo = '1' AND bar = 'Bar'\"\n// }\n```\n\nThe query is executed using **placeholders** in the database, which makes string-value concatenation safe. You can also obtain an embedded string version of the query so that you can easily debug it by copying and pasting.\n\n### Inject a Query Helper\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport { withQueryHelper } from 'query-weaver';\nimport pg from 'pg';\n\nconst db = withQueryHelper(new pg.Pool());\n\nconst foo = 1, bar = 'Bar';\nconst { rows } =\n  await db.query`SELECT * FROM foobar WHERE foo = ${foo} AND bar = ${bar}`;\n\nconsole.log(rows);\n// [ { foo: 1, bar: 'Bar' } ]\n\ndb.end();\n```\n\nThe `withQueryHelper` utility wraps the original object with a Query Helper, providing utility functions as if they were built into the original object. Essentially, these are shorthand functions that bridge the original `query` and the Query Weaver functions, with the most notable features being debugging and transaction support.\n\n## Utilities\n\n### WHERE Builder\n\n`WHERE_AND` / `WHERE_OR` / `AND` / `OR` / `WHERE` (`WHERE_AND` alias)\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport { sql, WHERE, OR } from 'query-weaver';\n\nconst a = 1, b = 'string', c = null, d = 5, e = false, f = [1, 2, 3, 4, 5];\nconsole.log(String(sql`SELECT * FROM foobar ${WHERE({ a, b, c }, OR({ d, e }))}`));\n// SELECT * FROM foobar WHERE ((a = '1') AND (b = 'string') AND (c IS NULL) AND (((d = '5') OR (e = false))))\n\nconst q = sql`SELECT * FROM foobar ${WHERE(\n  {\n    a: 10,\n    b: 'string',\n    c: sql`IS UNKNOWN`,\n    d: sql`BETWEEN ${a} AND ${d}`,\n  },\n  'e IS NULL',\n  sql`f = ANY (${f})`\n)}`;\nconsole.log(q.text);\n// SELECT * FROM foobar WHERE ((a = $1) AND (b = $2) AND (c IS UNKNOWN) AND (d BETWEEN $3 AND $4) AND (e IS NULL) AND (f = ANY ($5)))\n\nconsole.log(q.embed);\n// SELECT * FROM foobar WHERE ((a = '10') AND (b = 'string') AND (c IS UNKNOWN) AND (d BETWEEN '1' AND '5') AND (e IS NULL) AND (f = ANY (ARRAY['1','2','3','4','5'])))\n```\n\n### JSON Builder\n\n`json`\n\n```js\nimport pg from 'pg';\nimport { withQueryHelper, json } from 'query-weaver';\n\nconst db = withQueryHelper(new pg.Pool());\n\nconst id = 10;\nconst obj = { b: 'string', c: [1, 2, 'X'], d: { e: null, f: undefined } };\n\nconst row =\n  await db.getRow`SELECT * FROM jsonb_to_record(${json`{\"a\": ${obj}, \"b\": ${id}}`}) AS (a jsonb, b int);`;\n\nconsole.log(row);\n// {\n//   a: { b: 'string', c: [ 1, 2, 'X' ], d: { e: null } },\n//   b: 10,\n// }\n\ndb.end();\n```\n\n### VALUES Builder\n\n`buildValues` / `sql.values`\n\n```js\nsql.values([[1, 2, 3], ...]);            // =\u003e VALUES (1, 2, 3), (...), ...\nsql.values([{ a: 1, b: 2, c: 3 }, ...]); // =\u003e VALUES (1, 2, 3), (...), ...\n```\n\n### Key Builder\n\n`buildKeys` / `sql.keys`\n\n```js\nsql.keys({ a: 1, b: 2, c: 3 });        // =\u003e (a, b, c)\nsql.keys([{ a: 1, b: 2, c: 3 }, ...]); // =\u003e (a, b, c)\n```\n\n### Raw Builder\n\n`raw`\n\n```ts\nimport { sql, raw } from 'query-weaver';\n\nconsole.log(JSON.stringify(sql`SELECT * FROM foobar WHERE ${raw(\"bar LIKE '%something%'\")}`));\n// {\"text\":\"SELECT * FROM foobar WHERE bar LIKE '%something%'\",\"values\":[],\"embed\":\"SELECT * FROM foobar WHERE bar LIKE '%something%'\"}\n```\n\n### INSERT Builder and Helper\n\n`buildInsert` / `sql.insert` builder, and `insert` helper\n\n```js\nsql.insert(tableName, { ...fieldValuePairs }); // =\u003e sql`INSERT INTO ...`\nawait db.insert(tableName, { ...fieldValuePairs });  // =\u003e db.query`INSERT INTO ...`\n\n// Bulk insert\nsql.insert(tableName, [{ ...fieldValuePairs }, ...]); // =\u003e sql`INSERT INTO ... VALUES (...), (...), ...`\nawait db.insert(tableName, [{ ...fieldValuePairs }, ...]);  // =\u003e db.query`INSERT INTO ... VALUES (...), (...), ...`\n```\n\n### UPDATE Builder and Helper\n\n`buildUpdate` / `sql.update` builder, and `update` helper\n\n```js\nsql.update(tableName, { ...fieldValuePairs }, { ...whereCondition }); // =\u003e sql`UPDATE ...`\nawait db.update(tableName, { ...fieldValuePairs }, { ...whereCondition });  // =\u003e db.query`UPDATE ...`\n\n// Empty conditions throw for safety.\nawait db.update(tableName, { name: 'updated' }, { id: undefined });\n```\n\n### DELETE Builder and Helper\n\n`buildDelete` / `sql.delete` builder, and `delete` helper\n\n```js\nsql.delete(tableName, { ...whereCondition }); // =\u003e sql`DELETE FROM ...`\nawait db.delete(tableName, { ...whereCondition });  // =\u003e db.query`DELETE FROM ...`\n\n// Empty conditions throw for safety.\nawait db.delete(tableName, { id: undefined });\n```\n\n### Transaction Helper\n\n`begin` helper\n\n```js\nawait db.begin(async (c) =\u003e {\n    await c.delete(...);\n    await c.insert(...);\n});\n\nawait db.begin({\n    role: 'guest',\n}, async (c) =\u003e {\n    await c.delete(...);\n    await c.insert(...);\n});\n```\n\nIf an error occurs, the transaction is safely rolled back.\n**NOTE:** `begin` can be nested, but only the outermost transaction is effective.\n\n## Low-level APIs\n\nAn object created by the `sql` keyword behaves like a simple object with the following properties: `text`, `values`, and `embed`.\n\nIn addition, the object provides the following APIs:\n\n- `append(...)` / `push(...)`\n  - Appends a **raw SQL string** or an object created by `sql` to the query.\n- `join(glue = ', ')` / `prefix(prefix)` / `suffix(suffix)` / `empty(empty)`\n  - Sets the glue, prefix, suffix, or empty string for `toString()`, respectively.\n- `setSewingPattern(prefix, glue, suffix, empty)`\n  - Sets the sewing pattern for `toString()` all at once.\n- `toString(opts?)`\n  - Constructs the SQL string using the sewing pattern and any provided `opts` settings.\n\nTo create a query object, you can use:\n\n- `` sql`template string with ${value}` `` / `` json`{\"a\": \"b\", \"c\": ${value}}` ``\n  - Creates a query object with values that are automatically escaped.\n- `sql(value, ...)` / `json(value)`\n  - Creates a query object from values that are automatically escaped.\n\nThese APIs can be used, for example, to construct an `IN` clause as follows:\n\n```ts\nimport { sql } from 'query-weaver';\n\nconsole.log(sql`SELECT * FROM foobar WHERE foo IN (${sql(1, 2, 3).join()})`.embed);\n// SELECT * FROM foobar WHERE foo IN ('1', '2', '3')\n\nconst a = [1, 2, 3];\nconsole.log(sql`SELECT * FROM foobar WHERE foo IN (${sql(...a).join()})`.text);\n// SELECT * FROM foobar WHERE foo IN ($1, $2, $3)\n```\n\n### Caveats\n\n- Only `sql` and `json` accept a template string literal.\n- The actual SQL statement executed on the database may sometimes differ between `[.text, .values]` and `.embed` due to differences in serialization functions.\n\n### DEBUG\n\nYou can easily access the statements and results when using the Query Helper. For example, the following code records the session:\n\n\u003c!-- prettier-ignore --\u003e\n```js\nimport zlib from 'node:zlib'\n\nconst db = withQueryHelper(new pg.Pool(), {\n  onError: (ctx, error) =\u003e console.error(JSON.stringify({ error: zlib.gzipSync(JSON.stringify({ ...ctx, error })).toString('base64') })),\n  beforeQuery: (ctx) =\u003e console.log(JSON.stringify({ query: zlib.gzipSync(JSON.stringify(ctx)).toString('base64') })),\n  afterQuery: (ctx, result) =\u003e console.log(JSON.stringify({ result: zlib.gzipSync(JSON.stringify({ ...ctx, result })).toString('base64') })),\n});\n```\n\nAfter running this, you will see output similar to the following in the console:\n\n\u003c!-- prettier-ignore --\u003e\n```json\n{\"query\":\"H4sIAAAAAAACA6tWKkmtKFGyUgp29XF1DlHQUnAL8vdVSMvPT0osUgj3cA1yBXEUbBVUDJV0lMoSc0pTi5Wsog1jdZRSc5NSU4jRqm6oDtRblFpcmgO0qlopOT83NzEPoRUkmV/unF+aB5Q2BHNAdlQrAbWDBYAGAhU7Acna2NpaABybVha0AAAA\"}\n```\n\nYou can extract the contents by executing the following command:\n\n\u003c!-- prettier-ignore --\u003e\n```sh\n% echo '... base64part' | base64 -d | zcat | jq -r .\n\n# Or, if you're using X11, select the base64 part and then:\n% xclip -o | base64 -d | zcat | jq -r .\n```\n\n\u003c!-- prettier-ignore --\u003e\n```json\n{\n  \"text\": \"SELECT * FROM foobar WHERE foo = $1\",\n  \"values\": [\n    1\n  ],\n  \"embed\": \"SELECT * FROM foobar WHERE foo = '1'\",\n  \"result\": {\n    \"command\": \"SELECT\",\n    \"rowCount\": 1,\n    \"rows\": [\n      {\n        \"foo\": 1,\n        \"bar\": \"Bar\"\n      }\n    ]\n  }\n}\n```\n\nIt is very handy to replay the session as follows:\n\n```sh\n% xclip -o | base64 -d | zcat | jq -r .embed | psql\n foo | bar\n-----+-----\n   1 | Bar\n(1 row)\n\n# Or, you can create a shell script named `xclip-query` and then run:\n% xclip-query .embed | psql\n```\n\n### Query Helper with a Custom `query` Function\n\nThe underlying `query` function, which is used to perform queries, can be replaced by using the `query` option. This allows you to use Prisma, TypeORM, or even raw SQLite object with Query Helper. For example:\n\n\u003c!-- prettier-ignore --\u003e\n```js\nconst db = withQueryHelper(new PrismaClient(), {\n  query: QueryHelper.prisma,\n});\n\nconsole.log(await db.query`...`);\n```\n\n\u003c!-- prettier-ignore --\u003e\n```js\nconst db = withQueryHelper(new DataSource({ ... }), {\n  query: QueryHelper.typeorm,\n});\n\nconsole.log(await db.query`...`);\n```\n\n\u003c!-- prettier-ignore --\u003e\n```js\nimport { DatabaseSync } from 'node:sqlite';\n\nconst db = withQueryHelper(new DatabaseSync(), {\n  query: QueryHelper.sqlite,\n});\n\nconsole.log(await db.query`...`);\n```\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport { withQueryHelper } from 'query-weaver';\n\nconst queryable = async function (this: object, { text, values }: QueryConfig) {\n  return {\n    rows: [this], // `this` will be the object you passed to the constructor\n    rowCount: 1,\n  }\n}\n\nconst db = withQueryHelper({ some: 'Hello, object' }, {\n  query: queryable,\n});\n\nconsole.log(await db.getRow`HELLO QUERY`);\n// { some: 'Hello, object' }\n```\n\nThat's it!\nNow you can use Query Weaver interfaces on the objects.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkikuchan%2Fquery-weaver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkikuchan%2Fquery-weaver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkikuchan%2Fquery-weaver/lists"}