JavaScript
One import, one connection. Microsecond reads from L1 cache.
Install
npm install goldlapel pg
# or: npm install goldlapel postgres
# or: npm install goldlapel @vercel/postgres Gold Lapel is driver-agnostic — install whichever Postgres driver you prefer (pg, postgres, @vercel/postgres) and point it at gl.url.
Quick Start
import * as goldlapel from 'goldlapel';
import pg from 'pg';
// Start the proxy and get back an instance
const gl = await goldlapel.start('postgresql://user:pass@localhost:5432/mydb');
// gl.url is the proxy URL — use it with any Postgres driver
const client = new pg.Client({ connectionString: gl.url });
await client.connect();
const { rows } = await client.query('SELECT * FROM users');
// Or use wrapper methods — no conn argument needed
const hits = await gl.search('articles', 'body', 'postgres tuning');
await gl.docInsert('events', { type: 'signup' });
await gl.stop(); Repeated reads serve in microseconds from the built-in L1 cache.
After startup, Gold Lapel prints a one-line summary to stderr and serves a dashboard:
console.log(gl.dashboardUrl);
// "http://127.0.0.1:7933" (or null if not running / dashboard disabled) Banner goes to stderr so it never pollutes stdout-piped application output. Pass { silent: true } to start() to suppress it entirely — useful for daemons and structured-log pipelines that inspect both streams.
Auto-cleanup with await using
On Node 22+, use the TC39 explicit resource management proposal to auto-stop the proxy at scope end:
{
await using gl = await goldlapel.start('postgresql://...');
const hits = await gl.search('articles', 'body', 'hello');
} // proxy auto-stops here On older Node, call await gl.stop() explicitly.
Scoped connections
Use gl.using(conn, cb) to run a block against a specific connection — useful for transactions or per-request pools. The override survives across await boundaries (backed by AsyncLocalStorage), so nested async helpers pick up the same connection.
import pg from 'pg';
const pool = new pg.Pool({ connectionString: gl.url });
const client = await pool.connect();
await client.query('BEGIN');
await gl.using(client, async (gl) => {
await gl.docInsert('events', { type: 'order.created' });
await gl.incr('counters', 'orders');
});
await client.query('COMMIT');
client.release(); For a single call, pass { conn } as the last argument:
await gl.docInsert('events', { type: 'x' }, { conn: client }); Driver auto-detection
Gold Lapel auto-detects a Postgres driver at import time. It tries, in order:
pg(node-postgres)postgres(postgres.js)@vercel/postgres
The first one installed is used for the instance's internal default connection. Your own code is free to use any driver against gl.url.
If you don't need an internal connection (for example, you're only using Gold Lapel to provide a proxy URL to Prisma), pass noConnect: true to skip the driver lookup:
const gl = await goldlapel.start(upstream, { noConnect: true });
// now use gl.url with Prisma / Drizzle / your driver of choice API
await goldlapel.start(upstream, opts)
Factory that spawns the proxy and returns a GoldLapel instance. Eagerly opens an internal driver connection (unless noConnect: true).
upstream— your Postgres connection stringopts.port— proxy port (default: 7932)opts.dashboardPort— dashboard port (default: 7933;0disables)opts.logLevel—trace/debug/info/warn/error(string-based; translated to the binary's verbose flags internally)opts.config— camelCase config object (see Configuration)opts.extraArgs— raw CLI flags passed to the binaryopts.noConnect— skip opening the internal driver connectionopts.silent— suppress the stderr startup banner (default:false)opts.mesh— opt into the mesh at startup (HQ enforces the license; denial is non-fatal)opts.meshTag— optional mesh tag; instances sharing a tag cluster together
gl.url
Proxy URL string. Pass this to any Postgres driver.
gl.dashboardUrl
Dashboard URL (e.g. http://127.0.0.1:7933), or null if the dashboard is disabled or the proxy is not running.
gl.using(conn, callback)
Runs callback(gl) with conn as the implicit connection for any wrapper method invoked inside. Survives across await via AsyncLocalStorage. Every wrapper method also accepts a trailing { conn } option for one-off overrides.
await gl.stop() / gl[Symbol.asyncDispose]()
Stops the proxy and closes the internal connection. Idempotent. Also auto-runs on process exit.
configKeys()
Returns all valid config key names.
import { configKeys } from 'goldlapel';
console.log(configKeys()); Multiple instances
Each start() call spawns its own proxy subprocess and returns a fresh instance. Use different ports to run several side by side.
import * as goldlapel from 'goldlapel';
const gl1 = await goldlapel.start('postgresql://user:pass@localhost:5432/app_db', { proxyPort: 7932 });
const gl2 = await goldlapel.start('postgresql://user:pass@localhost:5432/analytics_db', { proxyPort: 7942 });
// Each instance manages its own proxy and cache
await gl1.stop();
await gl2.stop(); Configuration
Pass a config object under opts.config. Keys use camelCase and map to CLI flags (poolSize → --pool-size). Boolean keys are flags — true enables them. Array keys produce repeated flags. The logLevel option accepts string levels and translates to the binary's verbose flags internally.
const gl = await goldlapel.start('postgresql://user:pass@localhost/mydb', {
mode: 'waiter', // top-level: waiter | bellhop
logLevel: 'info', // top-level: trace | debug | info | warn | error
mesh: true, // top-level: opt into the mesh at startup
meshTag: 'prod-east', // top-level: optional mesh tag
config: { // structured tuning knobs only
poolSize: 50,
disableMatviews: true,
replica: ['postgresql://user:pass@replica1/mydb'],
},
}); Unknown keys throw immediately. See the configuration reference for the complete list.
Raw CLI flags
You can also pass raw CLI flags via extraArgs:
const gl = await goldlapel.start(upstream, {
extraArgs: ['--threshold-duration-ms', '200', '--refresh-interval-secs', '30'],
}); Environment variables
The binary also reads GOLDLAPEL_PROXY_PORT, GOLDLAPEL_UPSTREAM, and all other GOLDLAPEL_* env vars automatically. Set GOLDLAPEL_BINARY to override the binary location.
ORM & framework integrations
Gold Lapel is driver-agnostic, so any ORM or framework that speaks to Postgres works against gl.url.
- Prisma — install the
@goldlapel/prismasubpackage alongsidegoldlapel:npm install goldlapel @goldlapel/prisma prisma. See the Prisma guide. - Drizzle — install
@goldlapel/drizzlealongsidegoldlapel:npm install goldlapel @goldlapel/drizzle drizzle-orm. See the Drizzle guide. - Next.js / Express / plain Node — use
goldlapel.start()directly with any driver (pg,postgres,@vercel/postgres).
Upgrading from v0.1
v0.2 is a breaking redesign. The class-based new GoldLapel(url) + gl.start() shape and the module-level stop() / proxyUrl() / dashboardUrl() singletons are gone.
// v0.1 (old) — scoped package, class constructor + start()
import { GoldLapel } from '@goldlapel/goldlapel';
const gl = new GoldLapel(url);
await gl.start();
await gl.client.query('SELECT 1');
await gl.stop();
// v0.2 (new) — factory + bring your own driver
import * as goldlapel from 'goldlapel';
import pg from 'pg';
const gl = await goldlapel.start(url);
const client = new pg.Client({ connectionString: gl.url });
await client.connect();
await client.query('SELECT 1');
await gl.stop(); const gl = await goldlapel.start(url)replaces the constructor +start()pair.gl.url(property) replacesgoldlapel.proxyUrl()(module function).gl.dashboardUrl(property) replacesgoldlapel.dashboardUrl()(module function).await gl.stop()(instance) replacesgoldlapel.stop()(module).- Use
gl.using(conn, cb)or a trailing{ conn }option to override the wrapper's implicit connection instead ofgl.client. - Multiple instances are first-class — each
start()call spawns its own proxy.
Redis Replacement
Gold Lapel includes a Redis Replacement API — common Redis patterns backed by PostgreSQL. Every method hangs directly off gl and accepts an optional trailing { conn } option for transactional coordination.
Pub/Sub
// Pub/Sub
await gl.publish('orders', 'new order');
await gl.subscribe('orders', (msg) => {
console.log(msg);
}); Queues
// Queues
await gl.enqueue('jobs', { task: 'send_email' });
const job = await gl.dequeue('jobs'); Counters
// Counters
await gl.incr('page_views', 'home');
const count = await gl.getCounter('page_views', 'home'); Sorted Sets
// Sorted Sets
await gl.zadd('leaderboard', 'player1', 100);
const top = await gl.zrange('leaderboard', 0, 9); Streams
// Streams
const id = await gl.streamAdd('events', { type: 'signup' });
await gl.streamCreateGroup('events', 'workers');
const msgs = await gl.streamRead('events', 'workers', 'worker-1');
await gl.streamAck('events', 'workers', msgs[0].id); See the Redis Replacement docs for the complete API — including geospatial, rate limiting, sessions, and scripting.
Document Store
docFind, docInsert, docUpdate, docDelete and friends operate on JSONB-backed collections. Tables are auto-created on first use.
Filter operators
docFind supports the MongoDB filter operators you'd reach for — $elemMatch, $text, $gt, $in, and more.
// $elemMatch — scope multi-condition filters to a single array element
const orders = await gl.docFind('orders', {
items: { $elemMatch: { sku: 'ABC-123', qty: { $gte: 2 } } },
}); // $text — full-text search, document-wide or field-scoped
const hits = await gl.docFind('articles', {
$text: { $search: 'postgres tuning' },
}); See Appendix D: Filter Operator Reference for the full list, Postgres translations, and index notes.
Search
Full-text search utilities backed by PostgreSQL tsvector/tsquery. No extensions required.
Facets
// Facets — value counts, optionally filtered by a search query
const categories = await gl.facets('articles', 'category');
const filtered = await gl.facets('articles', 'category', {
query: 'machine learning', queryColumn: 'body'
}); Aggregations
// Aggregations — count, sum, avg, min, max with optional grouping
const byRegion = await gl.aggregate('orders', 'total', 'avg', {
groupBy: 'region'
}); Custom Search Config
// Custom search config
await gl.createSearchConfig('my_english', { copyFrom: 'english' }); Percolator
Store queries, then match documents against them. Like Elasticsearch percolate.
// Percolator — store queries, match documents against them
await gl.percolateAdd('alerts', 'breaking-news',
'breaking news earthquake', { metadata: { notify: 'slack' } });
const matches = await gl.percolate('alerts',
'A 6.2 magnitude earthquake struck the coast.');
await gl.percolateDelete('alerts', 'breaking-news'); Analyze
// Analyze — show tokenization pipeline
const tokens = await gl.analyze('The quick brown foxes'); Explain Score
// Explain score — score breakdown for a specific document
const result = await gl.explainScore('articles', 'body',
'machine learning', 'id', 42); See the API reference for full parameter details on all search methods.