{"id":24698625,"url":"https://github.com/faradayio/selectstar","last_synced_at":"2025-10-09T06:30:56.970Z","repository":{"id":65493837,"uuid":"312669116","full_name":"faradayio/selectstar","owner":"faradayio","description":"Generate safe SQL statements with tagged literals","archived":false,"fork":false,"pushed_at":"2021-10-18T13:51:11.000Z","size":203,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-08-09T15:45:58.488Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/faradayio.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-13T19:47:00.000Z","updated_at":"2022-11-04T21:36:19.000Z","dependencies_parsed_at":"2023-01-25T21:15:19.481Z","dependency_job_id":null,"html_url":"https://github.com/faradayio/selectstar","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/faradayio/selectstar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faradayio%2Fselectstar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faradayio%2Fselectstar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faradayio%2Fselectstar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faradayio%2Fselectstar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faradayio","download_url":"https://codeload.github.com/faradayio/selectstar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faradayio%2Fselectstar/sbom","scorecard":{"id":392725,"data":{"date":"2025-08-11","repo":{"name":"github.com/faradayio/selectstar","commit":"95709f305340fae6482c8a3660a813d3df628d66"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.3,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 1/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: license.txt:0","Info: FSF or OSI recognized license: MIT License: license.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":4,"reason":"6 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-qrpm-p2h7-hrv2","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T18:12:58.446Z","repository_id":65493837,"created_at":"2025-08-18T18:12:58.446Z","updated_at":"2025-08-18T18:12:58.446Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000825,"owners_count":26082950,"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","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"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":[],"created_at":"2025-01-27T04:29:31.977Z","updated_at":"2025-10-09T06:30:56.582Z","avatar_url":"https://github.com/faradayio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# selectstar\n\nSafe, idiomatic query generation for Postgres.\n\n## Installation\n\n```\nnpm install selectstar\n```\n\n## Usage\n\n`selectstar` is a library for generating sophisticated SQL within a Javascript\nor TypeScript program, while still making it easy to avoid SQL injection\nattacks and other common mistakes when generating queries with string\nconcatenation.\n\n```js\nimport { sql } from 'selectstar';\n\nsql`SELECT 1`;\n```\n\n`selectstar` uses [tagged template literals] to generate an object that you can\npass to node-postgres as a [parameterized query]. Above, we are generating the\nsimplest possible Postgres query, and it results in a simple object of two keys:\n\n```js\n{\n  text: 'SELECT 1',\n  values: []\n}\n``` \n\nYou can pass this directly into the `.query` methods of `pg`'s `Client` and\n`Pool` instances:\n\n```js\nawait client.query(sql`SELECT 1`)\n```\n\n### Parameters\n\nIf you pass templated variables into the query, they will appear in the\nresulting object as query parameters:\n\n```js\nconst id = 802728;\n\nsql`\n  SELECT id, first_name, last_name\n  FROM users\n  WHERE id = ${id}\n`;\n// returns:\n{\n  text: `\n    SELECT id, first_name, last_name\n    FROM users\n    WHERE id = $1\n  `,\n  values: [802728]\n}\n```\n\nThis introduces a layer of safety, because query parameters can't be used for\nsql injection attacks. You can insert any value into the template that Postgres\nsupports, which includes strings, numbers, arrays, objects, and dates.\n\n`selectstar` is ignorant of the underlying query semantics, which means that it\nwill handle more sophisticated Postgres queries gracefully as well:\n\n```js\n// use a CTE, selectstar doesn't care:\nconst query = sql`\n  WITH place_ids (id) AS (\n    SELECT p1.id\n    FROM places AS p1\n    JOIN places AS p2 ON p2.id = ${place_id}\n    WHERE\n      ST_Intersects(p1.the_geom, p2.the_geom)\n  )\n  SELECT * FROM places JOIN place_ids ON place_ids.id = places.id\n`;\n```\n\n### Templating (or: Dynamic Queries)\n\nSometimes you will want to generate a query that has a dynamic number of\nvariables, or optionally includes snippets. `selectstar` offers some tools to\nmake this easy and safe.\n\n#### Use `template` to construct query fragments:\n\nThe `template` function will create a snippet of SQL that can be inserted into\nlarger queries. These templates can contain variables, which will be lazily\nevaluated when the query is constructed:\n\n```js\nimport { subDays } from 'date-fns';\nimport { sql, template } from 'selectstar';\n\nconst yesterday = subDays(new Date(), 1); // subtract 1 day from today\nconst updatedSinceYesterday = template`updated_at \u003e ${yesterday}`;\n\nconst query = sql`SELECT * FROM users WHERE ${updatedSinceYesterday}`;\n// query =\n{\n  text: `SELECT * FROM users WHERE updated_at \u003e $1`.\n  values: [yesterday]\n}\n```\n\n#### Use `identifier` to dynamically-specify column or table names:\n\nThe `identifier` function will let you safely specify constants inline within a\nquery in places where parameters can't be used or are undesirable. Identifiers\nare escaped using the same algorithm as node-postgres. There are two main uses\nfor `identifier`: specifying dynamic columns or dynamic table names:\n\n```js\nconst vesselType = \"submarines\"; // or \"spaceships\", or \"sailboats\", etc\nconst query = `SELECT id, name FROM ${identifier(vesselType)}`;\n// query =\n{\n  text: `SELECT id, name FROM \"submarines\"`,\n  values: []\n}\n```\n\n#### Use `list` to generate lists of dynamic SQL:\n\nThe `list` function will let you transform a list of data into dynamic SQL. This\ncombines well with the previous functions:\n\n```js\nconst rows = [\n  { id: 1, first: \"Phillip\", last: \"Fry\" },\n  { id: 2, first: \"Turanga\", last: \"Leela\" },\n];\n\nconst rowSql = ({ id, first, last }) =\u003e\n  template`(${id}, ${first}, ${last}, now())`;\n\nconst query = `\n  INSERT INTO users (id, first_name, last_name, created_at) VALUES\n  ${list(rows.map(rowSql))}\n`;\n// query =\n{\n  text: `\n    INSERT INTO users (id, first_name, last_name, created_at) VALUES\n    ($1, $2, $3, now()),\n    ($4, $5, $6, now())\n  `\n}\n```\n\nThe default separator for `list` is the literal string `\", \"`. You can change\nthis by passing a different separator as the second argument:\n\n```js\nconst criteria = [\n  template`updated_at \u003e ${yesterday}`,\n  template`home_town = ${hometown}`\n];\n\nconst query = `\n  SELECT * FROM users WHERE\n  ${list(criteria, ' AND ')}\n`\n// query =\n{\n  text: `\n    SELECT * FROM users WHERE\n    updated_at \u003e $1 AND home_town = $2\n  `,\n  values: [yesterday, hometown]\n}\n```\n\n**Beware:** Do not pass dynamic or potentially-unsafe values to `separator`.\nValues passed to this function are treated as literal safe values. Recommend\nthat you only pass `\", \"`, `\" AND \"`, and `\" OR \"` to this second argument. The\ndefault is `\", \"`, which is appropriate in most circumstances.\n\n#### Use a function as an escape hatch\n\nYou may need to do something that isn't covered by the tools above, but you\nstill need the safety of parameterized queries. In this case, you can pass a\nfunction to a parameter, and it will be evaluated when the query is generated.\n\nThe function takes a single argument: an entrypoint that lets you generate\ndynamic SQL. Under the hood this is how the `template` function works.\n\n```js\nfunction columns(sql) {\n  const cols = ['id', 'name', 'speed'];\n  return sql`${list(cols.map(identifier))}`;\n}\n\nconst query = `SELECT ${columns} FROM starships`;\n// query =\n{\n  text: `SELECT \"id\", \"name\", \"speed\" FROM starships`,\n  values: []\n}\n```\n\n#### Use `unsafe` if you have no other choice\n\nSometimes you need to turn all the safeties off. In this case you can use the\n`unsafe` function to insert literal SQL into your queries. `unsafe` takes a\nstring, which will be inserted wholesale into the resulting query. Use at your\nown risk, `selectstar` cannot help you if you screw up.\n\n```js\nconst columns = ['id', 'name', 'speed'];\nconst query = `SELECT ${unsafe(columns.join(', '))} FROM starships`;\n// query =\n{\n  text: `SELECT id, name, speed FROM starships`,\n  values: []\n}\n```\n\n### Streaming\n\n`selectstar` is agnostic about how you use the generated query. As a result you\ncan use the result in some interesting ways. For example, the [pg-query-stream]\nlibrary allows you to pull records out of the database through a Nodejs readable\nobject stream. This lets you perform streaming transformations on large numbers\nof records as they are pulled from the database without requiring a ton of\nmemory.\n\n```js\nimport QueryStream from 'pg-query-stream';\nimport { sql } from 'selectstar';\n\nconst query = `SELECT id, first_name, last_name FROM users`;\nconst stream = new QueryStream(query.text, query.values);\nawait db.query(stream);\n\n// In Node, readable streams are also async iterators, which we can loop over\n// with `for await (...`\nfor await (const user of stream) {\n  console.log(`[${user.id}]  ${user.first_name} ${user.last_name}`);\n}\n// Logs:\n// [1]  Phillip Fry\n// [2]  Turanga Leela\n// ...\n```\n\nThis is especially useful if you want to do an [ETL] process where you're\nquerying data out of a database to be stored in another format or in a different\nDBMS. This pairs ideally with the [naushon] stream transformation library, which\nis used in the example below:\n\n```js\nimport { eduction, partitionBy } from 'naushon';\n\nconst query = `SELECT * FROM vessels`;\nconst stream = new QueryStream(query.text, query.values);\nawait db.query(stream);\n\n// Pour the results into tables based on their type using partitionBy:\nfor await (const vessels of eduction(partitionBy(v =\u003e v.type), stream)) {\n  const vessel = vessels[0];\n  const cols = Object.keys(vessel);\n  const rows = vessels.map(v =\u003e template`(${list(cols.map(c =\u003e v[c]))})`);\n\n  await db.insert(sql`\n    INSERT INTO ${identifier(vessel.type)} (${list(cols.map(identifier))})\n    VALUES ${list(rows)}\n  `);\n}\n```\n\n## Rationale\n\nSQL is a powerful language with rich semantics. Many object-relational and\nfluent query builder systems either only offer a subset of these semantics or\nobscure them behind complex forms. The intention behind `selectstar` is to give\ndevelopers as thin an interface for interacting with a SQL server as possible\nwhile still making it easy to create safe dynamic queries.\n\nThis library builds on the approach of [simple-postgres], which makes it\neasier to interact with a database with no configs. Rather than combine the\nconcern of connecting with a database and querying the database, `selectstar`\nleaves the client connection up to you. One advantage of this approach is that\n`selectstar` does not have a direct dependency on postgres (it has no\ndependencies at all), so you may upgrade the client library independently of\nthis one.\n\nAlso, because `selectstar` divorces query generation from query execution, you\nmay use the two in different contexts: the program or module that generates the\nSQL may not know about the underlying SQL connection. You may also use\n`selectstar`-generated queries with other Postgres-related libraries, like\n[pg-query-stream].\n\n`selectstar` is not a replacement for other query-generation tools like knex or\nan ORM, it's another tool in the toolbox for constructing powerful, idiomatic\nSQL queries.\n\nIt is possible that `selectstar` will work with different SQL clients or servers\nbut that is not its intended or supported use case at this time.\n\n[tagged template literals]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\n[parameterized query]: https://node-postgres.com/features/queries#parameterized-query\n[simple-postgres]: https://github.com/madd512/simple-postgres\n[pg-query-stream]: https://github.com/brianc/node-postgres/tree/master/packages/pg-query-stream/\n[ETL]: https://en.wikipedia.org/wiki/Extract,_transform,_load\n[naushon]: https://github.com/nhusher/naushon\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaradayio%2Fselectstar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaradayio%2Fselectstar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaradayio%2Fselectstar/lists"}