A PostgreSQL-backed OpenFeature provider. Works with
any runtime that supports pg (Node.js,
Deno, Bun, etc.).
Flags are served from an in-memory cache using a refresh-ahead pattern — the cache is proactively updated so evaluations never block on a database round-trip:
- LISTEN/NOTIFY — schema triggers send a Postgres notification on every flag change; the provider re-syncs immediately (debounced).
- Periodic sync — a jittered timer re-syncs as a fallback in case a notification is missed (e.g. during a connection drop).
Each provider instance holds one dedicated connection from the pool for
LISTEN. Size your pool accordingly.
graph LR
Start@{ shape: sm-circ }
Start -- "resolve" --> Provider
subgraph Provider["PostgresProvider"]
direction TB
Listener["NOTIFY Listener"]
Timer["Periodic Timer"]
Cache["Cache\n(in-memory)"]
end
PG["PostgreSQL"]
Cache -- "load all flags" --> PG
Timer -. "sync" .-> Cache
Listener -. "LISTEN" .-> PG
-. "NOTIFY on change" .-> Listener
-. "sync" .-> Cache
Rows in openfeature.flag_targeting define weighted variant distributions. Rows
with subject IS NULL are the flag-wide default; rows with a subject match
against EvaluationContext.targetingKey and resolve with reason
TARGETING_MATCH.
-- Pin one targeting key to a variant.
INSERT INTO openfeature.flag_targeting
(flag_key, subject, flag_type, variant, weight)
VALUES ('my-flag', 'user-42', 'string', 'beta', 1);See examples/basic.ts for a runnable walkthrough.
Apply schema.sql once to bootstrap. For version bumps that
change the schema, apply the matching script in migrations/
before deploying.
psql "$DATABASE_URL" -f schema.sql # initial install
psql "$DATABASE_URL" -f migrations/0.2.0-to-0.3.0.sql # upgradingSet statement_timeout on your pool to prevent hung queries from blocking the
provider indefinitely:
const pool = new pg.Pool({
connectionString: "...",
statement_timeout: 10_000, // 10s
});Apache-2.0