-
Notifications
You must be signed in to change notification settings - Fork 2
Drivers Neon
Postgres-over-HTTP for edge/serverless — a pool-free SQLConnectionEngine that
drives Neon's SQL-over-HTTP endpoint instead of a TCP socket. Fetch-only, so no
runtime-specific dependency and no raw socket.
NeonHttpEngine is "PostgresEngine over HTTP". Each execute() becomes a
single POST https://<host>/sql request (over
@tundralibs/restler → the runtime's native
global fetch), so it never opens a socket and stays edge/serverless-safe. It
emits Postgres SQL via the shared PostgresTranslator (Dialect = 'postgres')
and decodes result values with the shared Postgres text decoder — the emitted
SQL and decoded rows are identical to the socket-based
PostgresEngine; only the transport
differs.
Because a query is one standalone HTTP request, there is no session to carry a
transaction, prepared statement, advisory lock, or connection pool across
calls — those capabilities are declared false (see
Capabilities and Limitations).
Ships with @tundralibs/drivers — see the
package README.
// Per-engine subpath (keeps the edge bundle free of the TCP wire stack).
import { NeonHttpEngine } from '@tundralibs/drivers/neon';import { NeonHttpEngine } from '@tundralibs/drivers/neon';
const neon = new NeonHttpEngine('edge', {
host: 'ep-cool-name-a1b2c3.us-east-2.aws.neon.tech',
// Either a ready-made connection string …
connectionString: 'postgresql://user:pass@ep-cool-name-a1b2c3…/neondb',
// … or the discrete components (username + password + database):
// username: 'user',
// password: '...',
// database: 'neondb',
// Optional bearer JWT for Neon Authorize / RLS:
// token: '<jwt>',
// Optional per-request timeout in seconds (1–120, default 30):
// timeout: 30,
});
const r = await neon.execute({
sql: 'SELECT id, name FROM users WHERE id = :id:',
params: { id: 1 },
});
console.log(r.data);
await neon.disconnect();:name: placeholders are rewritten to Postgres $N markers and the ordered
values are sent as the request's params array. No network happens at
construction or on connect() — the client is stateless (one HTTP request per
query), which is exactly what makes it edge-safe.
Extends SQLEngineOptions.
host is always required (it forms the request URL); supply at least one
authentication mechanism — a connectionString, the
username+password+database components, or a bearer token. The
constructor throws MISSING_CONFIG_VALUE otherwise.
| Option | Type | Default | Notes |
|---|---|---|---|
host |
string |
— | Required. Neon endpoint host, e.g. ep-cool-name-a1b2c3.us-east-2.aws.neon.tech. The request URL is https://<host>/sql. |
connectionString |
string |
— | Full postgresql://user:password@host/db. Sent in the Neon-Connection-String header; the password in it authenticates. Takes precedence over components. |
username |
string |
— | Component auth: assembled into a connection string with password + database. |
password |
string |
— | Component auth (see username). |
database |
string |
— | Component auth (see username). |
token |
string |
— | Bearer JWT for Neon Authorize / row-level security. Sent as Authorization: Bearer <token>. May accompany a connection string or stand alone. |
timeout |
number |
30 |
Per-request timeout in seconds (1–120), passed through to RESTler. Must be a positive number. |
The pool-related fields on SQLEngineOptions (pool) are inert — this engine
is pool-free (one stateless HTTP client, no socket pool).
Read straight from the engine's Capabilities object:
| Capability | Value | Why |
|---|---|---|
transactions |
false |
One-shot HTTP — no session spans requests. |
preparedStatements |
false |
No session to hold a prepared statement. |
pooledConnections |
false |
Fetch-based; the platform pools, not the driver. |
advisoryLock |
false |
No session-scoped pg_advisory_lock over one-shot HTTP. |
referentialActions |
true |
Postgres enforces FK actions — a per-server fact. |
inPlaceAlter |
true |
Postgres accepts in-place ALTER COLUMN ... TYPE. |
Neon returns raw Postgres text (the client sets Neon-Raw-Text-Output: true),
which is decoded with the same OID → JS mapping the socket-based
PostgresEngine uses — see
Postgres → Type round-trips
(int8 → bigint, bool → boolean, json/jsonb → parsed object,
bytea → Uint8Array, timestamps → Date, numeric → string, …).
-
No interactive transactions (
transactions: false). Neon's/sqlendpoint runs one statement per HTTP request, sotransaction()/beginTransaction()reject withUNSUPPORTED_OPERATIONat the base guard, before any client is reserved. -
No prepared statements / advisory locks (
preparedStatements: false,advisoryLock: false) — both need a session that survives across requests. -
Native Postgres array columns are unsupported by the param encoder.
_encodeValueserializes objects and arrays as JSON, so an array-typed column (int[],text[], …) can't be bound directly — pass such a value pre-formatted as a Postgres array-literal string, or use ajsonbcolumn instead. Scalars (boolean/number/string),bigint(→ decimal string),Date(→ ISO-8601), andUint8Array(→\x-hexbytea) all encode natively. -
Connection string in a header. The
Neon-Connection-Stringheader carries the database password. It lives on the RESTler client, never on a thrown error —_wrapDriverErrorcopies only safe, query-relevant fields (sqlState,table,column,constraint,detail, the SQL text) — and RESTler redacts the header in its own error/log output.
Because the engine talks only over global fetch (RESTler never sets the
tls / socketPath transport options), it runs unchanged on socket-less edge
runtimes — Cloudflare Workers, Vercel Edge, Deno Deploy — and on Deno, Bun,
and Node. The @tundralibs/drivers/neon subpath deliberately imports none of
the Postgres TCP wire stack (PgConnection / protocol / binary / auth /
compat connect), so the edge bundle stays clean; its only heavy dependency is
@tundralibs/restler.
See the driver compatibility matrix for how Neon compares to the socket-based engines and what one-shot HTTP KEEPS / LOSES / DEGRADES for a higher-level consumer.
Postgres SQLSTATE codes (returned in Neon's error JSON) are mapped to standard
EngineError.code values via the shared SQLSTATE table — the same mapping the
socket-based PostgresEngine applies. See
Drivers → Standardized error codes.