Features
Library Overview
- Language — Pure JavaScript, with no native/binary dependencies to compile or ship.
- Strictly typed — Written entirely in TypeScript, with types shipped alongside the package.
- Modern module format — Ships as ESM; Node 22.12+ can
require()it from CommonJS code as well. - Promise-based API — Every asynchronous operation returns a promise instead of taking a callback.
- Rigorously tested — A test suite covering the wire protocol, every data type, and connection-handling edge case, run on every push across Node 22, 24 and 26 against PostgreSQL 12, 16 and 18, and under Bun against 18.
Features
- Connection Management — Supports both a single connection and a connection pool. See Single Connection and Connection Pooling.
- Binary Wire Protocol — Implements the full binary wire protocol for all PostgreSQL data types. See Data Types & Type Mapping.
- Exact Numerics & Rich Types —
numericandmoneydecode to anumberwhen a double holds the value exactly and to aNumericotherwise, never silently losing digits;decimalAsStringreads either as the exact decimal string instead, with no currency symbol or grouping. Geometric values (point,box,circle,line,path,polygon),interval, and the range/multirange family each decode to a class of their own that round-trips through its own type viatoPostgres()/toString(), and serializes as its own fields viatoJSON(). Native decoding for network types (inet/cidr/macaddr), bit strings,tsvector/tsquery/jsonpath, and the system-column types (tid,xid/xid8/cid,pg_lsn) too.unknownTypesAsStringasks the server for text on anything still unregistered instead of handing back aBuffer. See Data Types & Type Mapping. - Temporal Support —
temporalTypesdecodesdate/time/timestamp/timestamptz/intervalintoTemporal.PlainDate/PlainTime/PlainDateTime/ZonedDateTime/Durationinstead ofDate, with the microseconds PostgreSQL actually stores and no zone guessing. See Temporal Values. - Transaction Modes —
startTransaction(),transaction(fn)andPool.transaction(fn)all take the isolation level, read-only flag and deferrable flag on theBEGINitself, one round trip instead of two for a separateSET TRANSACTION. See Transaction Modes. - Reported Connection Loss — A backend killed by an administrator, a failover, or a network fault rejects the in-flight call and fires
Connection's'error'/'close'andPool's'destroy'/'error'events with aConnectionLostErrorcarryingcode: '08006'and the backend's process id — told apart from an application-requestedclose()by that code, without aninstanceofcheck. See Error Handling. - Server Notices — A
RAISE NOTICEor PostgreSQL's own "table does not exist, skipping" arrives as a'notice'event onConnection/Pool, shaped like aDatabaseErrorbut never rejecting the call it came from. See Error Handling: Server Notices. - Prepared Statements — Named prepared statements for optimized query execution, plus a per-connection cache that reuses one automatically for SQL the connection has run before — a repeated query costs
Bind/Executeinstead of parsing again, worth 3.2x on fifty concurrent calls and 2.9x again on a query inside an open transaction. On by default,prepare: falseto opt out. See Prepared Statements and Extended Query: Automatic Statement Caching. - Statement-Level Rollback — Inside a transaction, each statement runs under a savepoint of its own by default, so a failed statement leaves the transaction usable instead of aborting the whole block. The
SAVEPOINT/RELEASEpair travels in the statement's own round trip, so neither costs a round trip of its own.rollbackOnError: falseto opt out. See Transactions & Savepoints. - Scoped Transactions —
transaction(fn)commits when the callback returns and rolls back when it throws, on a connection or straight from the pool; a nested call takes a savepoint instead of a secondBEGIN. See Scoped Transactions. - Complete Results —
query()returns every row a statement produces by default. An explicitfetchCountlimits the rows returned, and the limit is never silent: the result carriessuspended: truewhen the server stopped there, so a prefix is never mistaken for the whole answer. SeefetchCountand suspended results. - Cursors — Cursors backed by a fast double-linked cache, iterable a row at a time with
for awaitand closed automatically when the loop ends. See Cursors. - Batch Execution —
executeBatch()runs one prepared statement over many parameter sets under a singleSync, reporting each set's row count — 1000 updates in 20ms where the same calls pipelined individually take 188ms. See Prepared Statements. - Notifications — A high-level API for PostgreSQL notifications (LISTEN/NOTIFY), for real-time data updates. Channel names are quoted and case-folded to match what the server actually delivers, instead of being restricted to a pattern. See Notifications.
- Values as Text —
fetchAsStringasks the server for specific columns in its own text format and hands the bytes back unparsed — any OID, exact and never reconstructed from a decoded value. See Fetching as String. - Extensibility — Data types and type mapping can be extended for custom requirements; a type can opt out of parameter type inference with
inferrable: falsewhile still decoding its own columns. See Data Types & Type Mapping. - Parameter Binding — Bind parameters with OID mappings for precise and efficient query execution. See Query Parameters.
- Array Handling — Supports multidimensional arrays with fast binary encoding/decoding.
- Performance Optimization — Shared buffers keep memory use low and improve performance. See Benchmarks.
- Authorization — Supports password authentication methods including cleartext, MD5, and SASL (SCRAM-SHA-256). See SSL/TLS & Authentication.
- Bulk Import/Export —
COPY TO STDOUTandCOPY FROM STDINas Node streams, with backpressure in both directions, pluscopyFromRows(), which encodes rows straight into binaryCOPY. That is around 4x faster than the CSV equivalent, with no text escaping to get wrong. It takes arrays or objects from anything iterable, so a file larger than memory streams in. See COPY TO / COPY FROM. - Query Pipelining — Pooled queries can share connections so a burst isn't capped by pool size, opt-in per call.
pipeline()goes further for a known set of statements: several different ones travel under a singleSync, so they cost one round trip instead of one each and commit or roll back together. This is around 2x faster than the same calls throughPromise.all(), which is already pipelined. Statements the connection has run before bind to their cached prepared names, so a repeated set sends noParseat all — worth 2.5x on twenty statements. See Connection Pooling and Extended Query: Multi-Statement Pipelines. - Dynamic SQL — A
sqltag builds statements from composable fragments: values become parameters, names are quoted, andsql.values()/sql.set()write INSERT and UPDATE clauses from objects. See The sql Template Tag. - Cloudflare Workers — Runs on workerd under
nodejs_compatwith nothing else installed, TLS included (through the runtime's ownstartTls(), for servers whose certificate chains to a public CA), and Hyperdrive for everything else. Since 3.14.0. See Cloudflare Workers. - Multiple Hosts — A connection can list several servers and pick one by role (
targetSessionAttrs), so a cluster that has failed over is found on the next connect. See Multi-Host & Failover. - Large Objects — File-like access to binary data stored outside the row: seek, partial reads, streams, for values past what a
byteacolumn can hold. See Large Objects. - Logical Replication —
LogicalReplicationstreams committed row changes as an async iterable, decodingpgoutputitself, with client-side filtering and positions confirmed as you consume. See Logical Replication. - Channel Binding — SCRAM authentication binds itself to the TLS channel when the server offers it, the way libpq does by default, so a relayed login is detected even where the certificate isn't verified. See SSL/TLS & Authentication.
- Two-Phase Commit —
prepareTransaction()leaves a transaction waiting under a name forcommitPrepared()/rollbackPrepared(), from any connection. See Two-Phase Commit. - Cancellation — Any call takes an
AbortSignal, which also gives per-query timeouts viaAbortSignal.timeout(). See Cancellation & Timeouts. - Flexible Data Retrieval — Can return both array and object rows to suit different data processing needs.
- Resource Management — Automatic disposal of resources with the
usingsyntax (TC39 Explicit Resource Management). See Resource Management. - Long Cancel Key — Opt in to protocol 3.2 (PostgreSQL 18+) with
longCancelKey, so acancel()in progress can't be forged by an attacker guessing a short secret key. See Cancellation & Timeouts. - Legacy Function Call Protocol —
callFunction()calls a function by OID directly over the wire, bypassing SQL entirely, kept for protocol completeness even thoughSELECT func(...)covers the same ground. See Function Call Protocol. - Graceful Protocol Renegotiation — A server that doesn't recognize a requested protocol version or startup option reports back instead of erroring out, surfaced on
Connection.protocolNegotiation. See Cancellation & Timeouts. - Observability — Queries, scripts, prepared statements, pipelines, cursor fetches, COPY, transaction commands, pool acquires and connection attempts are published on Node's
diagnostics_channel, with five more channels for pool activity; nothing is published while nobody listens.postgrejs-otelturns them into OpenTelemetry spans and database client metrics, with an optional sqlcommenter comment that ties a row inpg_stat_activityto its trace. Since 3.15.0. See Tracing Channels and OpenTelemetry.
Feature Comparison
How postgrejs compares to pg (node-postgres) and postgres (postgres.js) — versions compared: postgrejs 3.15.0, pg 8.23.0, postgres.js 3.4.9. ✅ built in · 🟡 partial or needs a separate package · ❌ not supported.
| Feature | postgrejs | pg | postgres.js |
|---|---|---|---|
| Packaging | |||
| Packages to install | 1 | 4 | 1 |
| Module system | ESM | ESM/CJS | ESM/CJS |
| Language | TS | JS | JS |
| Cloudflare Workers | ✅ | 🟡 | ✅ |
| Wire protocol | |||
| Protocol version | 3.2 | 3.0 | 3.0 |
| Simple Query protocol | ✅ | ✅ | ✅ |
| Extended Query protocol | ✅ | ✅ | ✅ |
| Text wire format | ✅ | ✅ | ✅ |
| Binary wire format | ✅ | 🟡 | ❌ |
| Per-column format selection | ✅ | ❌ | ❌ |
| Long cancel key (opt-in) | ✅ | ❌ | ❌ |
| Legacy Function Call protocol | ✅ | ❌ | ❌ |
| Graceful protocol renegotiation | ✅ | ❌ | ❌ |
| High-level API | |||
| Object and array row modes | ✅ | ✅ | ✅ |
| Dynamic SQL helpers | ✅ sql tag | ❌ | ✅ |
| Per-query type mapping | ✅ | ❌ | ❌ |
| Query cancellation | ✅ AbortSignal | ✅ | ✅ |
| Per-query timeout | ✅ AbortSignal | ✅ | ❌ |
| Reference counters | Connection · Statement | ❌ | ❌ |
| Caller kept in async error stacks | ✅ | 🟡 | 🟡 |
| Error located in the SQL text | ✅ line and mark | 🟡 offset | 🟡 offset |
| TC39 Explicit Resource Management | ✅ | ❌ | ❌ |
| Querying | |||
| Query parameters | ✅ | ✅ | ✅ |
| Parameter type casting | ✅ | 🟡 | ✅ |
| Prepared statements | ✅ automatic | ✅ manual | ✅ automatic |
| Batch execution | ✅ | ❌ | ❌ |
| Multi-statement round trip | ✅ | ❌ | ❌ |
| Multi-statement scripts | ✅ | ✅ | ✅ |
| Server-side cursors | ✅ | 🟡 | ✅ |
COPY TO / COPY FROM | ✅ | 🟡 | ✅ |
| Binary COPY encoding | ✅ | ❌ | ❌ |
| Row count after a COPY | ✅ | ✅ | ❌ |
| Transaction management | |||
| Transaction API | ✅ | ❌ | ✅ |
| Savepoints | ✅ | ❌ | ✅ |
| Statement-level rollback | ✅ automatic | ❌ | 🟡 manual |
| Scoped transaction helper | ✅ nests | ❌ | ✅ |
| Two-phase commit API | ✅ | ❌ | 🟡 |
| Session management | |||
| Built-in connection pool | ✅ | ✅ | ✅ implicit |
| Pipelining on one connection | ✅ opt-in/call | ✅ opt-in/client | ✅ automatic |
| Graceful shutdown | ✅ | ❌ | ✅ |
| Multiple hosts | ✅ | ❌ | ✅ |
| LISTEN/NOTIFY | ✅ | 🟡 | ✅ |
| Data types | |||
| Text encoders | 125 | generic | 14 |
| Text decoders | 125 | 44 | 12 |
| Binary encoders | 121 | ❌ | ❌ |
| Binary decoders | 125 | 16 | ❌ |
| Multidimensional arrays | ✅ binary | 🟡 text | 🟡 text |
| Security | |||
| SSL/TLS | ✅ | ✅ | ✅ |
| Direct TLS negotiation (PG17) | ✅ | ✅ | ✅ |
| Cleartext, MD5, SCRAM-SHA-256 | ✅ | ✅ | ✅ |
SCRAM channel binding (-PLUS) | ✅ default | ✅ opt-in | ❌ |
| Beyond querying | |||
| Logical replication | ✅ | 🟡 | ✅ |
| Large object API | ✅ | ❌ | ✅ |
| Native libpq bindings | ❌ | 🟡 | ❌ |
| Observability | |||
diagnostics_channel events | ✅ | ❌ | ✅ |
| OpenTelemetry instrumentation | ✅ postgrejs-otel | 🟡 | ❌ |
| Spans beyond queries and connects | ✅ | ❌ | ❌ |
| Database client metrics | ✅ operation and pool | 🟡 | ❌ |
| Trace context in the SQL comment | ✅ | 🟡 | ❌ |
Every row was checked against each library's own source rather than its documentation. One row looks past the versions above: postgres.js's diagnostics_channel events arrived in 4.1.0. For the full footnotes explaining each 🟡/❌ (e.g. exactly what pg's binary decoder is missing, or why postgres.js has no per-query timeout), see the Feature Comparison section of the postgrejs README.
For real-world performance numbers backing up "Performance Optimization" above, see Benchmarks.