← Docs

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:

  1. pg (node-postgres)
  2. postgres (postgres.js)
  3. @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 string
  • opts.port — proxy port (default: 7932)
  • opts.dashboardPort — dashboard port (default: 7933; 0 disables)
  • opts.logLeveltrace / 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 binary
  • opts.noConnect — skip opening the internal driver connection
  • opts.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/prisma subpackage alongside goldlapel: npm install goldlapel @goldlapel/prisma prisma. See the Prisma guide.
  • Drizzle — install @goldlapel/drizzle alongside goldlapel: 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) replaces goldlapel.proxyUrl() (module function).
  • gl.dashboardUrl (property) replaces goldlapel.dashboardUrl() (module function).
  • await gl.stop() (instance) replaces goldlapel.stop() (module).
  • Use gl.using(conn, cb) or a trailing { conn } option to override the wrapper's implicit connection instead of gl.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.