layout: doc lang: en translation_key: node-postgres title: Batch row lookups with node-postgres seo_title: “Batch PostgreSQL Row Lookups with node-postgres” description: Use authenticated RESP MGET from Node.js for cached row reads and node-postgres for SQL writes and ordinary queries. section: Node.js permalink: /docs/node-postgres.html

last_modified_at: “2026-09-16”

Batch row lookups with node-postgres {#batch-row-lookups-with-node-postgres}

Read rows by primary key using your existing node-postgres connection or pool.

Start the demo, install dependencies, and run its integration assertions:

npm --prefix examples/node-postgres ci --ignore-scripts
npm --prefix examples/node-postgres run demo

Read through RESP {#send-one-parameterized-query}

Given a connected RESP client from @redis/client:

const ids = [42, 7, 42, null, 999999];
const wireKeys = ids.filter(id => id !== null).map(id =>
  `CRUD:app.public.items:${JSON.stringify({ id })}`
);
const values = await client.mGet(wireKeys);
let position = 0;
const rows = ids.map(id => {
  if (id === null) return null;
  const value = values[position++];
  return value === null ? null : JSON.parse(value);
});

RESP MGET returns JSON-encoded rows in key order. The helper omits null input keys and restores their positions; missing keys return null.

Keep the table name fixed in application code. Pass IDs as query parameters, not SQL assembled from strings. See node-postgres documentation for parameters and named prepared statements.

The RESP command accepts at most 1,024 keys. The runnable helper returns [] without a request when all inputs are null. PostgreSQL bigint and numeric fields in JSON can exceed JavaScript’s exact numeric range; use a lossless JSON parser or an explicit serialization contract for such values.

Compare with the existing batch query {#compare-with-the-existing-batch-query}

The baseline uses:

SELECT id::text AS key, row_to_json(i)::text AS row
FROM public.items AS i
WHERE id = ANY($1::bigint[]);

ANY does not preserve input order or duplicate requested positions. The example restores them on the client and supplies null for missing rows before comparing results.

The runnable implementation is in examples/node-postgres. The helper takes an existing client rather than creating a pool per call.

Transactions and application boundaries {#transactions-and-application-boundaries}

Use one acquired client throughout a transaction. Reads after writes in the same transaction use PostgreSQL’s source-table path. The demo checks this with separate reader and writer connections; see cache invalidation.

Prepared statements and result caching {#prepared-statements-and-result-caching}

A named node-postgres query reuses a prepared statement on each connection. It does not cache returned rows. RESP MGET uses the extension’s shared whole-row cache, but its worker role and session state are separate from the application’s SQL connection. See the caching decision guide and batch lookup guide.

For RESP2, use the Node.js RESP example. Recorded Node.js results include batch reads and concurrent updates. The common benchmark runs Node.js and Go through the same prepared SQL and RESP scenarios.