Skip to content

JavaScript

View Markdown

You have two ways to reach your database from JavaScript: the PostgreSQL wire protocol via the pg driver, or the HTTP query API via fetch. Choose based on where your code runs and whether you need transactions.

Set your key in the environment before starting:

Terminal window
export PZ_API_KEY='pz_live_REPLACE_ME'

The pg driver (node-postgres) gives you a real connection pool and transaction support. The database name includes the /, and TLS is disabled.

Terminal window
npm install pg
import { Client } from 'pg';
const client = new Client({
host: 'db.database.pizza',
port: 5432,
user: 'u', // ignored; any value
password: process.env.PZ_API_KEY,
database: 'acme/production', // discrete field — no %2F encoding
ssl: false, // TLS is declined by the proxy
});
await client.connect();
await client.query(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
await client.query(
'INSERT INTO users (name, email) VALUES ($1, $2)',
['Ada Lovelace', 'ada@acme.example'],
);
const res = await client.query(
'SELECT * FROM users WHERE name = $1',
['Ada Lovelace'],
);
console.log(res.rows);
// [{ id: 1, name: 'Ada Lovelace', email: 'ada@acme.example' }]
await client.end();

Use $1, $2 placeholders with pg; never interpolate values into the SQL string.

For a long-running server, use a Pool instead of a single Client:

import { Pool } from 'pg';
const pool = new Pool({
host: 'db.database.pizza',
port: 5432,
user: 'u',
password: process.env.PZ_API_KEY,
database: 'acme/production',
ssl: false,
max: 10,
});
const { rows } = await pool.query('SELECT COUNT(*) AS n FROM users');

The HTTP API needs no driver, so it works in the browser, in edge functions, and in serverless runtimes. It does not support transactions.

const KEY = process.env.PZ_API_KEY; // or import.meta.env for Vite/browser
async function query(sql, params = []) {
const res = await fetch(
'https://db.database.pizza/acme/production/query',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ sql, params }),
},
);
if (!res.ok) {
const { error } = await res.json();
throw new Error(`query failed (${res.status}): ${error}`);
}
return res.json();
}
const result = await query(
'SELECT * FROM invoices WHERE status = ?',
['open'],
);
console.log(result.columns); // [{ name: 'id', type: 'INTEGER' }, …]
console.log(result.rows); // [[1, 1, 4500, 'open', '…'], …]

Use ? placeholders with the HTTP params array. $1 placeholders are supported by PostgreSQL drivers, not by the HTTP parameter substitution path.

It is technically possible to call the HTTP API from a browser, but that means shipping your API key to end users. Don’t embed a live key in client-side code. Route browser requests through your own backend, which holds the key server-side. Use a tightly scoped key (for example read-only, or a REST_API key with per-table limits) as a defense in depth. See API keys & permissions.

Because the wire protocol is PostgreSQL-compatible, ORMs that speak Postgres generally work — as long as you keep the SQL to the SQLite-compatible subset and set the connection as shown above. Drizzle and Prisma’s Postgres providers can connect with the URI from Connect; be prepared to adjust migrations away from Postgres-specific types (SERIAL, TEXT[], JSONB). Check Compatibility before generating schema.