# @suaveplan/api-plugin-database - SurrealDB-backed database plugin for @suaveplan/api. Pools @suaveplan/surrealdb's SurrealAdapter (implementing @suaveplan/db DatabaseAdapter), runs .surql migrations through @suaveplan/api-migrations under an advisory lock, provides tenant provisioning callables, retry/backoff, RFC 7807 problem-json rendering, and a full OTel surface. Path A wrapper — adapter surface delegates to suaveplan SurrealAdapter; this plugin owns the orthogonal pool/migration/tenancy/retry/observability concerns. SurrealDB-backed database plugin for `@suaveplan/api`. Pools `@suaveplan/surrealdb`'s `SurrealAdapter` (implementing `@suaveplan/db`'s `DatabaseAdapter`), runs `.surql` migrations through `@suaveplan/api-migrations` under an advisory lock, ships tenant-provisioning callables for the three canonical multi-tenancy strategies, and emits a complete OpenTelemetry surface (pool checkouts / checkins / saturation + query duration histogram). RFC 7807 problem-json rendering rounds out the error path. This is a **Path-A wrapper**: every adapter call delegates to the canonical `SurrealAdapter`; this package owns only the orthogonal pool / migration / tenancy / retry / observability concerns. Consumers obtain the `DatabaseAdapter` through `ctx.getService("db")` and call the suaveplan surface defined by `@suaveplan/db`. ## Documentation tree - [config](./src/config/config-schema.md) — Defines the Zod schemas that validate every public option bundle accepted by `@suaveplan/api-plugin-database`. The sche… - [config-schema](./src/config/config-schema.md) — Defines the Zod schemas that validate every public option bundle accepted by `@suaveplan/api-plugin-database`. The sche… - [error-codes](./src/error-codes/error-codes.md) — Defines the stable `DATABASE_*` error-code registry consumed by every other module in the `@suaveplan/api-plugin-databa… - [error-codes](./src/error-codes/error-codes.md) — Defines the stable `DATABASE_*` error-code registry consumed by every other module in the `@suaveplan/api-plugin-databa… - [lock](./src/migrations/lock.md) — Implements the advisory lock that prevents two concurrent migration runners from racing each other. The lock is a singl… - [runner](./src/migrations/runner.md) — Implements `runMigrations`, the top-level orchestrator for the migration phase of `createDatabasePlugin`'s `onReady` ho… - [otel](./src/otel/instruments.md) — Provides the lazily-cached bundle of OpenTelemetry instruments emitted by `@suaveplan/api-plugin-database`. Every count… - [instruments](./src/otel/instruments.md) — Provides the lazily-cached bundle of OpenTelemetry instruments emitted by `@suaveplan/api-plugin-database`. Every count… - [plugin](./src/plugin/plugin.md) — Exports `createDatabasePlugin`, the top-level factory that produces the `PluginDescriptor` consumed by `@suaveplan/api-… - [plugin](./src/plugin/plugin.md) — Exports `createDatabasePlugin`, the top-level factory that produces the `PluginDescriptor` consumed by `@suaveplan/api-… - [pool](./src/pool/pool.md) — Implements `ConnectionPool`, a bounded pool of `SurrealAdapter` instances that backs the `db` service registered by `cr… - [extended-adapter](./src/pool/extended-adapter.md) — Defines `PooledSurrealAdapter`, a thin subclass of `@suaveplan/surrealdb`'s `SurrealAdapter` that exposes the underlyin… - [pool](./src/pool/pool.md) — Implements `ConnectionPool`, a bounded pool of `SurrealAdapter` instances that backs the `db` service registered by `cr… - [retry](./src/retry/retry.md) — Provides `withRetry`, a small exponential-backoff helper used by the connection pool, the migration runner, and any cal… - [retry](./src/retry/retry.md) — Provides `withRetry`, a small exponential-backoff helper used by the connection pool, the migration runner, and any cal… - [routes](./src/routes/problem-json.md) — Renders arbitrary throwables as RFC 7807 `application/problem+json` bodies for HTTP handlers that wrap database calls.… - [problem-json](./src/routes/problem-json.md) — Renders arbitrary throwables as RFC 7807 `application/problem+json` bodies for HTTP handlers that wrap database calls.… - [services](./src/services/registry-augmentation.md) — Augments the `ServiceRegistry` interface from `@suaveplan/api-plugin` to register the `db` slot typed as `@suaveplan/db… - [registry-augmentation](./src/services/registry-augmentation.md) — Augments the `ServiceRegistry` interface from `@suaveplan/api-plugin` to register the `db` slot typed as `@suaveplan/db… - [tenancy](./src/tenancy/tenancy.md) — Exports the `provisionTenant` and `decommissionTenant` callables that downstream hosts (typically `@suaveplan/api-plugi… - [tenancy](./src/tenancy/tenancy.md) — Exports the `provisionTenant` and `decommissionTenant` callables that downstream hosts (typically `@suaveplan/api-plugi… - [types](./src/types/types.md) — Houses the internal type aliases and shared interfaces consumed by the rest of the package — the adapter factory seam,… - [types](./src/types/types.md) — Houses the internal type aliases and shared interfaces consumed by the rest of the package — the adapter factory seam,… ## Install ```bash bun add @suaveplan/api-plugin-database ``` ## Quick start ```ts import { createPluginRegistry } from "@suaveplan/api-plugin"; import { createDatabasePlugin } from "@suaveplan/api-plugin-database"; const registry = createPluginRegistry(); registry.register( createDatabasePlugin({ url: "ws://localhost:8000/rpc", namespace: "app", database: "main", username: process.env.SURREAL_USER ?? "root", password: process.env.SURREAL_PASS ?? "root", pool: { min: 2, max: 20, idleMs: 30_000, acquireTimeoutMs: 5_000 }, migrations: { autoRun: true, directory: "./surql", ledgerTable: "_migrations", lockTable: "_migration_lock", leaseMs: 60_000, }, }) ); await registry.initialize(); // Inside any other plugin const db = ctx.getService("db"); const users = await db?.query("SELECT * FROM user"); ``` ## Modules - [`plugin/plugin.md`](./src/plugin/plugin.md) — top-level factory `createDatabasePlugin`. - [`pool/pool.md`](./src/pool/pool.md) — bounded `ConnectionPool` over `SurrealAdapter`. - [`pool/extended-adapter.md`](./src/pool/extended-adapter.md) — `PooledSurrealAdapter` subclass surfacing `getRawClient()`. - [`migrations/runner.md`](./src/migrations/runner.md) — `.surql` runner under advisory lock. - [`migrations/lock.md`](./src/migrations/lock.md) — advisory-lock primitive. - [`tenancy/tenancy.md`](./src/tenancy/tenancy.md) — `provisionTenant` / `decommissionTenant` callables. - [`retry/retry.md`](./src/retry/retry.md) — exponential-backoff helper. - [`routes/problem-json.md`](./src/routes/problem-json.md) — RFC 7807 renderer. - [`config/config-schema.md`](./src/config/config-schema.md) — Zod-validated configuration boundary. - [`error-codes/error-codes.md`](./src/error-codes/error-codes.md) — `DATABASE_*` stable error registry. - [`otel/instruments.md`](./src/otel/instruments.md) — OpenTelemetry instrument bundle. - [`services/registry-augmentation.md`](./src/services/registry-augmentation.md) — `ServiceRegistry["db"]` module augmentation. ## Tenancy Three strategies are supported via `tenancy.strategy`: - `shared` — single namespace + single database; rows carry a `tenant_id` discriminator. - `namespace-per-tenant` — one SurrealDB namespace per tenant. - `database-per-tenant` — one SurrealDB database per tenant inside the active namespace. Tenant lifecycle is exposed as callable exports (`provisionTenant` / `decommissionTenant`) rather than descriptor hooks because the `PluginDescriptor` has no tenant-lifecycle slots. Wire them to your host's tenant events. ## Observability Counters / histograms emitted under the `@suaveplan/api-plugin-database` meter: - `db.pool.checkouts_total` - `db.pool.checkins_total` - `db.pool.saturation` (up-down-counter) - `db.query.duration_ms` (histogram, `{ outcome }` label) Migration metrics are emitted by `@suaveplan/api-migrations` under its own meter. ## Errors Every failure path raises `DatabaseError` carrying one of eight `DATABASE_*` codes: `CONNECTION_FAILED`, `POOL_EXHAUSTED`, `POOL_CLOSED`, `QUERY_FAILED`, `MIGRATION_FAILED`, `LOCK_CONTENDED`, `TENANT_INVALID`, `INVALID_CONFIG`. See [`error-codes/error-codes.md`](./src/error-codes/error-codes.md).