Server: Node / Bun client
Two standard Postgres clients, no Twill-specific driver: Bun ships Bun.sql; Node uses pg (node-postgres). Both connect to engine-server with an ordinary DSN and sslmode=disable.
Prerequisites
A running server (--listen 127.0.0.1:5433 --db file://./srv.db). On Node: npm i pg. On Bun: nothing — Bun.sql is built in.
Bun — Bun.sql
import { SQL } from "bun";
const sql = new SQL("postgresql://[email protected]:5433/main?sslmode=disable");
// Tagged-template values are sent as bind parameters (extended protocol).
const rows = await sql`SELECT id, body FROM notes ORDER BY id DESC LIMIT 50`;
await sql`INSERT INTO notes (body, ts) VALUES (${"hello"}, ${new Date().toISOString()})`;
Breakdown: the ${...} interpolations are not string concatenation — Bun.sql turns them into Parse/Bind parameters, which the server's extended protocol handles. That's both safe (no injection) and what lets the engine plan the statement.
Node — node-postgres (pg)
import { Client } from "pg";
const client = new Client({
host: "127.0.0.1",
port: 5433,
user: "postgres",
database: "main",
ssl: false, // cleartext — the listener does not terminate TLS
});
await client.connect();
const { rows } = await client.query(
"SELECT id, body FROM notes WHERE id = $1",
[1],
);
await client.query(
"INSERT INTO notes (body, ts) VALUES ($1, $2)",
["hello", new Date().toISOString()],
);
await client.end();
Breakdown: node-postgres uses $1, $2… numbered placeholders bound from the array — the server supports this placeholder form. ssl: false matches the cleartext listener.
Transactions
// node-postgres
await client.query("BEGIN");
try {
await client.query("UPDATE accounts SET balance = balance - $1 WHERE id = $2", [100, 1]);
await client.query("UPDATE accounts SET balance = balance + $1 WHERE id = $2", [100, 2]);
await client.query("COMMIT"); // returns once the engine's WAL is durable
} catch (e) {
await client.query("ROLLBACK");
throw e;
}
Breakdown: the engine runs real transactions; COMMIT blocks until durable. A 40001-class serialization/conflict error (another writer committed first) is safe to retry — re-run the whole transaction.
Pooling
For a web app, use pg.Pool (Node) or rely on Bun.sql's built-in pool. Because the engine serializes writers, place a transaction-mode pooler in front for serverless bursts — see Connection pooling.
import { Pool } from "pg";
const pool = new Pool({ host: "127.0.0.1", port: 5433, user: "postgres", database: "main", ssl: false, max: 10 });
const { rows } = await pool.query("SELECT count(*) FROM notes");