Writing robust data pipelines with AILivestream July 22

Skip to main content

Connect from Node.js via Postgres endpoint

Preview
This feature is in preview and is subject to change.

You can query MotherDuck from Node.js using node-postgres (pg) — no DuckDB installation required.

For connection parameters, SSL options, and limitations, see the Postgres Endpoint reference.

Prerequisites

You'll need a MotherDuck access token. Set it as an environment variable:

export MOTHERDUCK_TOKEN="your_token_here"

Install the pg package:

npm install pg

Connect

Use a configuration object to connect. Do not pass sslrootcert=system in a connection string — node-postgres tries to read system as a file path and throws an ENOENT error.

import pg from "pg";

const client = new pg.Client({
host: "pg.us-east-1-aws.motherduck.com",
port: 5432,
user: "postgres",
password: process.env.MOTHERDUCK_TOKEN,
database: "md:",
ssl: { rejectUnauthorized: true },
});

await client.connect();
const { rows } = await client.query(
"SELECT title, score FROM sample_data.hn.hacker_news WHERE type='story' LIMIT 10"
);
console.log(rows);
await client.end();

Connection pooling and timeouts

Use pg.Pool in production. Set a connection timeout so requests fail fast when new connections cannot be opened, set an idle timeout so unused connections are recycled quickly, and set a query timeout so one slow query does not let requests pile up.

import pg from "pg";

const pool = new pg.Pool({
host: "pg.us-east-1-aws.motherduck.com",
port: 5432,
user: "postgres",
password: process.env.MOTHERDUCK_TOKEN,
database: "md:",
ssl: { rejectUnauthorized: true },
max: 10,
connectionTimeoutMillis: 5_000,
idleTimeoutMillis: 30_000,
maxLifetimeSeconds: 300,
query_timeout: 60_000,
});

pool.on("error", (err) => {
console.error("Unexpected idle client error", err);
});

const { rows } = await pool.query(
"SELECT title, score FROM sample_data.hn.hacker_news WHERE type='story' LIMIT 10"
);
console.log(rows);

For simple queries, prefer pool.query(...); node-postgres checks out and releases the connection for you. When you check out a client manually, always release it. If the client hits a connection-level error such as a network reset, protocol error, or server termination, destroy it with client.release(true) instead of returning it to the pool.

const client = await pool.connect();
let destroy = false;

try {
await client.query("BEGIN");
await client.query("SELECT 1");
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK").catch(() => {
destroy = true;
});
throw err;
} finally {
client.release(destroy);
}

statement_timeout is not supported through the Postgres endpoint today. Use query_timeout for client-side cancellation.

SSL notes

Node.js uses the operating system's certificate store by default. Setting ssl: { rejectUnauthorized: true } tells node-postgres to use TLS and verify the server certificate against these trusted roots — this is the equivalent of sslmode=verify-full with sslrootcert=system in libpq.

If you need to specify a custom CA certificate (for example, the ISRG Root X1 certificate from Let's Encrypt):

import fs from "fs";

const client = new pg.Client({
host: "pg.us-east-1-aws.motherduck.com",
port: 5432,
user: "postgres",
password: process.env.MOTHERDUCK_TOKEN,
database: "md:",
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync("/path/to/isrgrootx1.pem").toString(),
},
});

For more details on SSL options, see SSL and certificate verification.

Cloudflare Workers

Cloudflare Workers use a different socket implementation (pg-cloudflare) that handles SSL differently. See Connect from Cloudflare Workers for Workers-specific setup.