# Slonik: a Node.js PostgreSQL client that validates rows at runtime and refuses unsafe connections

> Slonik is a TypeScript-first PostgreSQL client for Node.js built around raw SQL, runtime row validation and guards against leaked connections. It suits teams that want SQL in the source, not a query builder, and it costs you a schema library at every query boundary.

**gajus/slonik** — A Node.js PostgreSQL client with runtime and build time type safety, and composable SQL.

- Repository: https://github.com/gajus/slonik
- Stars: 4,940 · Forks: 156
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/gajus-slonik

## The problem Slonik solves: SQL you can read, rows you can trust

Most Node.js PostgreSQL code fails in one of two places. Either the SQL is assembled by a query builder and nobody can tell what will reach the server, or the SQL is plain and the row shapes are a guess. Slonik takes a position on both. Its stated principles are that it "promotes writing raw SQL" and "discourages ad-hoc dynamic generation of SQL", and the README links an essay titled Stop using Knex.js to make the case against builders. The audience is a team that is comfortable with PostgreSQL and uncomfortable with an abstraction between the two.

The second half is runtime validation. TypeScript types vanish when the process starts, so a column that changes from text to jsonb, or a join that starts returning nulls, is invisible until something downstream breaks. Slonik lets you attach a schema to a query and check each row as it comes back, which turns a silent shape change into an error at the call site. That is the trade: you keep the SQL, and you pay for a parser on the results.

## How the sql tag, the pool and the interceptors fit together

The central object is a connection pool created from a DSN. Queries are written with the sql template tag, and values are interpolated as placeholders rather than concatenated, which is the mechanism behind the README's "safe value interpolation" claim. The tag is composable: sql.fragment, sql.identifier, sql.join, sql.array, sql.json, sql.jsonb, sql.unnest and friends let you build a statement out of typed pieces without ever pasting a string into the query. sql.unsafe exists for the cases where you accept that risk deliberately.

Around the pool sit the guards. A client checked out of the pool has to be released, and transactions have to be opened and closed through the transaction method rather than by hand; the README frames these as protection against unsafe connection handling and unsafe transaction handling. Transactions nest, emit events, and can be retried, and individual queries can be retried too.

Interceptors are the extension point. They wrap query execution, and the repository ships separate packages for this: slonik-interceptor-query-logging is published from the same monorepo, alongside slonik-sql-tag-raw. The result parser interceptor is the one that powers sql.type, which is how a schema library gets attached to a query. Errors are mapped into named classes such as NotFoundError, UniqueIntegrityConstraintViolationError, ForeignKeyIntegrityConstraintViolationError, StatementTimeoutError and BackendTerminatedError, so a catch block can distinguish a missing row from a dead connection without parsing the driver's message text.

## Installing Slonik and running a first validated query

The package is published to npm as slonik. The README documents creating a pool from a connection URI and ending that pool when the process shuts down, and it documents describing the current state of the pool. A minimal setup looks like this.

## The runtime validation penalty and the shape of the schema dependency

Slonik does not ship its own validation language. The README's runtime validation section names a result parser interceptor and shows sql.type, and the tips section discusses compiling Zod schemas at build time, hoisting them with babel-plugin-zod-hoist, and validating queries with eslint-plugin-slonik. In practice that means adopting Slonik usually means adopting a schema library next to it, and the README has a section titled "Performance penalty" that acknowledges the cost of parsing every row. For a dashboard query returning a handful of rows this is noise. For a bulk export returning hundreds of thousands, per-row validation is a real expense, and the sensible move is to validate the queries where a shape change would be dangerous and leave the bulk paths untyped.

There is a second, quieter cost. The README documents behaviour for unknown keys in validated results, and that behaviour is a policy decision you inherit: a column added to a table may or may not break a query depending on how the schema is written. Teams that expect additive migrations to be free should read that part before writing their first schema.

## Where Slonik is the wrong tool

Slonik is a client, not a data layer. The README points readers to a separate Migrations section rather than presenting migrations as a core feature, and there is no relations graph, no lazy loading, no entity identity map. If your application is mostly CRUD over a schema you would rather not think about, an ORM will produce working code faster, and Slonik's insistence on hand-written SQL will read as friction rather than discipline.

The connection guards are also opinionated in a way that can bite. Code that holds a client across an await boundary, or that opens a transaction and returns a promise from inside it without awaiting, runs against the grain of the library. The guards exist precisely because those patterns are common, but a codebase already full of them will need rewriting rather than a drop-in swap.

Finally, the repository is a pnpm workspace with packages split across types, utilities, errors, sql-tag, driver, pg-driver and slonik, and the release notes show the satellite packages versioned in lockstep with the core (slonik@49.10.10, slonik-sql-tag-raw@49.10.10, slonik-interceptor-query-logging@49.10.10 on 2026-09-21). That is a large surface for a database client, and pinning versions across the set is part of the upgrade work.

## Slonik against pg, pg-promise and postgres

The README devotes a section to exactly these comparisons, so the differences are the author's own framing rather than an outside judgement. Against pg, the underlying driver, the difference is everything Slonik adds on top: placeholders through the sql tag, cardinality-specific query methods, connection and transaction guards, interceptors and mapped error classes. Plain pg gives you a client and a pool and leaves those decisions to you.

Against pg-promise, both offer a tagged-template query interface. Slonik's distinguishing additions are the runtime row validation through sql.type and the interceptor pipeline; pg-promise's model centres on its own formatting and helpers. The choice comes down to whether you want a schema library in the query path.

Against postgres, often referred to as postgres.js, the split is similar in shape but different in emphasis. The postgres package is known for its own template-tag API; Slonik's pitch is the validation layer, the assertion helpers and the error taxonomy. If you want the smallest possible surface over PostgreSQL, Slonik is the heavier of the two, and the runtime validation you are paying for is the thing you would be giving up.

## Maintenance, releases and the licence question

The repository is not archived, and the last push was on 2026-09-21, two days before this was written. Releases are managed with Changesets, and the recent release list shows core and satellite packages published together at version 49.10.10. The monorepo uses pnpm workspaces, with linting split across knip, oxlint and oxfmt, and a Husky directory for git hooks; the root package.json exposes lint:knip, lint:oxlint and lint:oxfmt behind a single lint script. For an adopter, the practical upgrade cost is that the version number is shared across the packages you install, so a bump touches the core and any interceptor or sql-tag package at the same time.

The licence is the loose end. The repository metadata reports NOASSERTION rather than a recognised identifier, even though a LICENSE file exists at the root. This is not legal advice, but before shipping Slonik in a product you should open that file and read the actual terms rather than trusting the package metadata, and check whether the satellite packages carry the same terms.

## Conclusion

Adopt Slonik if your team already writes SQL by hand and wants row shapes checked at runtime rather than trusted from a generated client. Skip it if you need a query builder, an ORM with migrations and relations, or a driver that hides SQL behind method calls; the README points at pg, pg-promise and postgres for those comparisons. Before committing, check the LICENSE file at the repository root, since the package metadata reports NOASSERTION rather than a named licence, and confirm that your Node version and your chosen schema library are supported by the current release line.

## FAQ

### What does slonik mean?

The README has a section titled Origin of the name, so the project treats the question as expected, but the cleaned README text does not state the etymology itself. Read that section in the repository for the author's explanation.

### How does Slonik compare with pg?

The README includes a pg vs slonik section. Slonik is built on top of a driver and adds the sql template tag for safe value interpolation, cardinality-specific query methods, connection and transaction guards, interceptors and mapped error classes.

### How does Slonik compare with TypeORM?

The README compares Slonik with pg, pg-promise and postgres, and does not cover TypeORM. What can be said from the README is that Slonik promotes writing raw SQL and discourages ad-hoc dynamic generation of SQL, which is a different approach from an entity-based ORM.

### How does Slonik compare with postgres.js?

The README has a postgres vs slonik section. Slonik's stated features centre on runtime validation, assertions and type safety, safe connection and transaction handling, interceptors and mapped errors, which is the ground on which the two are compared there.

### what does slonik mean

This is the same question as above. The README's Origin of the name section is the place the author addresses it, and the cleaned README text does not repeat the explanation.

## Sources

- [gajus/slonik on GitHub](https://github.com/gajus/slonik)
- [Issues](https://github.com/gajus/slonik/issues)
- [README](https://github.com/gajus/slonik/blob/main/README.md)
- [Releases](https://github.com/gajus/slonik/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gajus-slonik
