Creating a Provider
Each provider is a self-contained unit that includes driver + connection manager. SQL providers (provider-pg-node, provider-sqlite-node) delegate SQL generation to @jsorm/compiler, which is dialect-configurable. Only IndexedDB keeps its own non-SQL compiler.
You can create custom providers. The contract is small and verified against the real implementation.
Provider contract
Section titled “Provider contract”The core only requires a Provider:
// @jsorm/core — src/provider/types.tsexport interface Provider { name: string; execute<T = unknown>(ast: QueryAST): Promise<ProviderResult<T>>;}
export type ProviderResult<T, Meta = {}> = { data: T; sql?: string; params?: SqlParam[]; meta?: Meta;};The factory returns { provider } & ProviderExtras:
type ProviderExtras = { raw?: (sql: string, params?: unknown[]) => Promise<unknown> | unknown; close?: () => void; syncSchema?: () => SchemaReport | Promise<SchemaReport>; getMissingTables?: () => string[] | Promise<string[]>; compile?: (ast: QueryAST) => { sql: string; params: unknown[] };};Every extra is optional. sqlite-node and pg-node expose the full surface; indexeddb marks raw?: never; all?: never to signal it has no raw SQL. The client’s ProviderManager calls extras only if present and throws a clear “Provider does not support X()” error otherwise. So a valid minimal provider is just { provider }.
The provider execute receives a QueryAST and must resolve every operation. Look at provider-indexeddb: its execute switches on ast.operation (create, createMany, get, first, cursor, count, exists, update, updateMany, delete, deleteMany, junctionInsert, junctionDelete, junctionToggle).
Minimal example — REST-backed provider
Section titled “Minimal example — REST-backed provider”import type { Provider, ProviderResult, QueryAST } from "@jsorm/core";
type Options = { baseUrl: string };
export function createRestProvider(models: readonly ModelDefinition[], options: Options) { const provider: Provider = { name: "rest-node", async execute<T = unknown>(ast: QueryAST): Promise<ProviderResult<T>> { const res = await fetch(`${options.baseUrl}/${ast.context.table}`, { method: httpMethodFor(ast.operation), body: JSON.stringify({ op: ast.operation, data: ast.data ?? ast.rows }), }); return { data: (await res.json()) as T }; }, }; return { provider, close: () => {} };}Requirements
Section titled “Requirements”- Receives
QueryASTfrom the client - Converts AST to target format (SQL, REST, etc.)
- Executes against the database
- Returns
{ data: T } - Cannot know models, config, or other layers beyond its constructor args
Naming convention
Section titled “Naming convention”provider-{database}-{runtime}Example: provider-pg-node, provider-indexeddb
