usm-adapter-postgres
v0.4.8
Published
Universal Schema Model — Postgres/knex adapter: knex-backed collections, the where/order_by SQL compiler, and a LISTEN/NOTIFY change source
Downloads
1,420
Readme
usm-adapter-postgres
Universal Schema Model — the Postgres/knex adapter for usm-core.
Provides the concrete, SQL-facing half of the engine:
KnexCollectionSearcher— implements theusm-coreSearchercontract: compiles Hasura-stylewhere/order_by/limit/offsetand aggregates into knex queries and hydrates rows. Relationship filters become correlatedEXISTSsubqueries;wherecount predicates become correlated count subqueries.WhereBuilder/OrderByBuilder— the query compiler.KnexModelCollection— extends core'sModelCollection, implementing the batched key loader against a Postgres table via knex. App collections extend this and declarestatic searchFields/searchRelationships.knexSearchSchema(model)— reflects the model's collections (via core) and wires them to a knex searcher factory.NotifyChangeSource+PostgresDatabaseListener— aLISTEN/NOTIFYchange source for near-real-time subscriptions.installChangeTriggers(knex, { tables, channel })— installs statement-level triggers that emit one NOTIFY per mutating statement (the table name as payload). Bulk-load safe.connectKnexClient/connectPostgresClient— connection helpers.
Install
npm install usm-adapter-postgres knex pgknex and pg are peer dependencies.
Usage
import { KnexModelCollection, knexSearchSchema, connectKnexClient } from "usm-adapter-postgres";
import { ModelType } from "usm-core";
class AuthorType extends ModelType {
constructor(p, m){ super(m); this._p = p; }
get id(){ return this._p.id; } get name(){ return this._p.name; }
books(){ return this.model.collections.Book.search({ where: { author_id: { _eq: this.id } } }); }
}
class AuthorCollection extends KnexModelCollection {
static searchFields = { id: { type: "ID" }, name: { type: "String" } };
static searchRelationships = {
books: { kind: "array", target: "Book", localColumn: "id", remoteColumn: "author_id" }
};
constructor(model){ super(model, model.typeClasses.Author, "authors", { keyColumns: ["id"] }); }
}
class Model {
constructor(knex){
this.knex = knex;
this._typeClasses = { Author: AuthorType /* , ... */ };
this._collections = { Author: new AuthorCollection(this) /* , ... */ };
this._searchSchema = knexSearchSchema(this);
}
get collections(){ return this._collections; }
get typeClasses(){ return this._typeClasses; }
get searchSchema(){ return this._searchSchema; }
log(){}
}
const knex = await connectKnexClient({ url: process.env.DB_URL });
const model = new Model(knex);
await model.collections.Author.search({ where: { name: { _ilike: "%a%" } }, limit: 10 });Subscriptions
import { NotifyChangeSource, PostgresDatabaseListener, connectPostgresClient, installChangeTriggers } from "usm-adapter-postgres";
// once, against the DB you own:
await installChangeTriggers(knex, { tables: ["authors", "books"], channel: "app_change" });
// at startup:
const listener = new PostgresDatabaseListener(await connectPostgresClient({ url: process.env.DB_URL }));
const changeSource = new NotifyChangeSource(listener, { channel: "app_change", tables: "*" });
await changeSource.start();
// hand changeSource to a usm-core SubscriptionManagerLocal development note
The inter-package dependency on usm-core is declared as file:../usm-core for
in-repo development. When publishing, replace it with a version range (e.g.
"usm-core": "^0.1.0"), or use a workspace / npm install --install-links.
