HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
gajus
slonik
TypeScript2019–2026
gajus / slonik

slonik

A Node.js PostgreSQL client with runtime and build time type safety, and composable SQL.

24Distinct people
39Mentions
1Explicit recommenders
4.9KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

A Node.js PostgreSQL client with runtime and build time type safety, and composable SQL.

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

You're being downvoted, but I completely agree. If you are on Node and Postgres I highly recommend using slonik - it makes it easy to just write SQL but at the same time makes…

hn_throwaway_99 used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 5:04:57 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year39 dated mentions

Observed mentions: 2019-01-30 18:04:57 UTC to 2026-02-21 09:11:47 UTC.

0369122019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 3 mentions; 1 submissions, 2 comments20192020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 12 mentions; 1 submissions, 11 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments20222023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 11 mentions; 1 submissions, 10 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 4 mentions; 1 submissions, 3 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC312
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC303
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC12111
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC303
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC11110
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC413
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC202
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC101

What Hacker News says

39 sources
There's two different approaches to solving sql composability issues: 1. Compiles-to-SQL domain specific languages. This category spans from ORM DSLs embedded in another programming language, like Ruby/Rail's ActiveRecord/AREL or Django's ORM; to stand-alone text-based languages like PRQL Pipelined Relational Query Language" (https://prql-lang.org) that a compiler program converts to SQL text or SQL files. The downside to the DSL option is that it requires practitioners be fluent in both the SQL query they want, and in the DLS language - to know how to obtain the SQL query in the DSL. 2. Query fragment literals in the caller programming language, like sql`name = ${name}` in TypeScript (eg https://github.com/gajus/slonik). These are usually thin abstraction over concatenating a `{ queryText: string[], queryArgs: T[] }` structure. The autho
Read the rest
r only needs to be fluent in SQL, and in the caller language, but do less to save you from the expressive limitations of SQL itself. I've found query fragment composition to be the sweet spot. Easy SQL queries remain trivial to express and understand, since it's Just SQL: sql`SELECT * FROM block WHERE id = ${args.id}` But you can DRY up repetition in the codebase through regular function calls. Abbreviated example from Notion's client code: function selectOfflinePageMetadata(args: { userId: string }) { return sql` SELECT offline_page.id, offline_page.space_id, offline_page.download_status, offline_page.last_downloaded_at, offline_page.last_downloaded_version, offline_page.last_downloaded_sync_cursor, offline_page.target_sync_cursor, CASE WHEN EXISTS ( SELECT 1 FROM offline_action WHERE offline_action.impacted_page_id = offline_page.id AND offline_action.origin_page_id = offline_page.id AND offline_action.autosync_type = 'not_autosynced' ) THEN 1 ELSE 0 END AS is_explicitly_offlined_origin, CASE WHEN EXISTS ( SELECT 1 FROM offline_action WHERE offline_action.impacted_page_id = offline_page.id AND offline_action.origin_page_id = offline_page.id AND offline_action.autosync_type = 'created_offline' ) THEN 1 ELSE 0 END AS is_offline_created_origin, CASE WHEN EXISTS ( SELECT 1 FROM offline_action WHERE offline_action.impacted_page_id = offline_page.id AND offline_action.origin_page_id = offline_page.id AND offline_action.autosync_type = 'frecent' ) THEN 1 ELSE 0 END AS is_autosynced_origin FROM offline_page WHERE offline_page.meta_user_id = ${args.userId} ` } function selectOfflinePageById(args: { userId: string pageId: string }) { const { userId, pageId, } = args return sql` SELECT * FROM (${selectOfflinePageMetadata({ userId })}) WHERE id = ${pageId} ` } function selectOfflineAutosyncedOrigins(args: { userId: string spaceId: string }) { const { userId, spaceId, } = args return sql` WITH offline_page_metadata AS ( ${selectOfflinePageMetadata({ userId })} ) SELECT offline_page_metadata.* FROM offline_page_metadata WHERE space_id = ${spaceId} AND is_autosynced_origin = 1 ORDER BY last_downloaded_at ASC ` } I'm not sure if it solves your "view problem", but it does a pretty good job for _my_ view problem.
In Parrot – type-safe SQL in Gleam, supports SQlite, PostgreSQL and MySQL
ardsh·Sep 6, 2024Submission

Show HN: TypeScript SQL Query API Engine for Postgres

Some time ago I started working on this query API engine, a sort of layer on top of slonik[1], to abstract the most tedious parts of using straight SQL (dynamic filtering, paginating etc). The idea was to make it easy to create typesafe querying APIs, similar to ORMs I was using at the time, but using pure SQL. I was also inspired by hasura's GraphQL API engine, but wanted to build something simpler for tRPC APIs, thus the name. I've also built a small playground where you can see the raw SQL queries that are created when you run the API [2] It's a proof of concept that I've used to build APIs with a lot of dynamic filters and complex sub-queries, I'd love to know your thoughts on it! [1]: https://github.com/gajus/slonik [2]: https://sql-api-playground.netlify.app/
1 points · Open discussion
Because it turns into a parameterized SQL query. This already exists in the Javascript ecosystem: sql`select * from users where id = ${id}` Turns into: { query: 'select * from users where id = $1', values: [id] } So if you tried an injection like this: sql`select * from users ${'where id = 3'}` It turns into an invalid statement since "where id = 3" cannot exist as a parameterized value for the same reason this doesn't work: { query: 'select * from users $1', values: ['where id = 3'] } Where you go from here is to offer a query(statement) function that requires the use of the tag string so that you can't accidentally pass in a normal string-interpolated string. Examples: - slonik: https://github.com/gajus/slonik?tab=readme-ov-file#protectin... - postgres.js: https://github.com/porsager/postgres?tab=readme-ov-file#quer.
Read the rest
In PEP 750: Tag Strings for Writing Domain-Specific Languages
You can already use postgres with Slonik. https://github.com/gajus/slonik#user-content-slonik-how-are-... It is not going to be the default because it is way slower. https://github.com/gajus/slonik/actions/runs/6616647651 Test node_version:18 test_only:postgres-integration is taking 3 minutes. Test node_version:18 test_only:pg-integration is taking 38 seconds. It is possible that this is an issue with https://github.com/gajus/postgres-bridge, but I was not able to pinpoint anything in particular that would explain the time difference.
In PostgresJs: PostgreSQL client for Node.js and Deno
I've found libraries like Zod useful when interacting with external data sources like a database. Slonik[1] uses Zod to define the types expected from a SQL query and then performs runtime validation on the data to ensure that the query is yielding the expected type. I don't think it's necessary to use Zod/runtime validation everywhere, but it's a nice tool to have on hand. [1]https://github.com/gajus/slonik
In We use TypeScript not based on preference, but because we want to make money
Have you tried Slonik (https://github.com/gajus/slonik)? It won't generate types from queries automatically, but it encourages writing SQL vs. a query builder and allows type annotations of queries with Zod. Query results are validated at runtime to ensure the queries are typed correctly.
In The Epic Stack
Definitely a lot of misconceptions around how this would work. Just check out something like slonik, https://github.com/gajus/slonik, which is an excellent implementation. The example you gave actually isn't valid, because what you're doing is generating SQL dynamically, and that doesn't work the way prepared statements work. That is, you can't have a prepared statement like "select foo from bar where zed = ? order by ? asc", because with prepared statements the question marks can only substitute for VALUES, not schema names. So if you wanted to do something like that it slonik, it would fail. With slonik you CAN do dynamic SQL, that is guaranteed to be safe and checked at compile time with TypeScript, because you can nest SQL tagged templates. That is you can do this: const colToSortBy = useFoo ? sql`foo` : sql`bar`; const query
Read the rest
= sql`select col from mytable order by ${colToSortBy}`; In that case slonik will know how to safely "merge" the parent and child parsed SQL.
In JavaScript and TypeScript features of the last 3 years

Showing the 15 most recent of 39 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.