Skip to main content

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 — numeric and money decode to a number when a double holds the value exactly and to a Numeric otherwise, never silently losing digits; decimalAsString reads 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 via toPostgres()/toString(), and serializes as its own fields via toJSON(). 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. unknownTypesAsString asks the server for text on anything still unregistered instead of handing back a Buffer. See Data Types & Type Mapping.
  • Temporal Support — temporalTypes decodes date/time/timestamp/timestamptz/interval into Temporal.PlainDate/PlainTime/PlainDateTime/ZonedDateTime/Duration instead of Date, with the microseconds PostgreSQL actually stores and no zone guessing. See Temporal Values.
  • Transaction Modes — startTransaction(), transaction(fn) and Pool.transaction(fn) all take the isolation level, read-only flag and deferrable flag on the BEGIN itself, one round trip instead of two for a separate SET 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' and Pool's 'destroy'/'error' events with a ConnectionLostError carrying code: '08006' and the backend's process id — told apart from an application-requested close() by that code, without an instanceof check. See Error Handling.
  • Server Notices — A RAISE NOTICE or PostgreSQL's own "table does not exist, skipping" arrives as a 'notice' event on Connection/Pool, shaped like a DatabaseError but 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/Execute instead 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: false to 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/RELEASE pair travels in the statement's own round trip, so neither costs a round trip of its own. rollbackOnError: false to 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 second BEGIN. See Scoped Transactions.
  • Complete Results — query() returns every row a statement produces by default. An explicit fetchCount limits the rows returned, and the limit is never silent: the result carries suspended: true when the server stopped there, so a prefix is never mistaken for the whole answer. See fetchCount and suspended results.
  • Cursors — Cursors backed by a fast double-linked cache, iterable a row at a time with for await and closed automatically when the loop ends. See Cursors.
  • Batch Execution — executeBatch() runs one prepared statement over many parameter sets under a single Sync, 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 — fetchAsString asks 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: false while 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 STDOUT and COPY FROM STDIN as Node streams, with backpressure in both directions, plus copyFromRows(), which encodes rows straight into binary COPY. 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 single Sync, 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 through Promise.all(), which is already pipelined. Statements the connection has run before bind to their cached prepared names, so a repeated set sends no Parse at all — worth 2.5x on twenty statements. See Connection Pooling and Extended Query: Multi-Statement Pipelines.
  • Dynamic SQL — A sql tag builds statements from composable fragments: values become parameters, names are quoted, and sql.values()/sql.set() write INSERT and UPDATE clauses from objects. See The sql Template Tag.
  • Cloudflare Workers — Runs on workerd under nodejs_compat with nothing else installed, TLS included (through the runtime's own startTls(), 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 bytea column can hold. See Large Objects.
  • Logical Replication — LogicalReplication streams committed row changes as an async iterable, decoding pgoutput itself, 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 for commitPrepared()/rollbackPrepared(), from any connection. See Two-Phase Commit.
  • Cancellation — Any call takes an AbortSignal, which also gives per-query timeouts via AbortSignal.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 using syntax (TC39 Explicit Resource Management). See Resource Management.
  • Long Cancel Key — Opt in to protocol 3.2 (PostgreSQL 18+) with longCancelKey, so a cancel() 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 though SELECT 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-otel turns them into OpenTelemetry spans and database client metrics, with an optional sqlcommenter comment that ties a row in pg_stat_activity to 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.

Featurepostgrejspgpostgres.js
Packaging
Packages to install141
Module systemESMESM/CJSESM/CJS
LanguageTSJSJS
Cloudflare Workers✅🟡✅
Wire protocol
Protocol version3.23.03.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 countersConnection · 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 encoders125generic14
Text decoders1254412
Binary encoders121❌❌
Binary decoders12516❌
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.