@xemahq/biome-database-nest
v0.5.1
Published
Layer 1 SDK that centralizes ALL database concerns for Xema biome services. Provides the single correct construction of a schema-qualified Prisma driver-adapter (the second-arg `{ schema }` that makes `@prisma/adapter-pg` qualify `biome_<id>.<table>` inst
Downloads
9,512
Readme
@xemahq/biome-database-nest
Centralized database wiring for Xema biome services.
This package belongs to Layer 1 — it depends only on Layer 0 contracts and other Layer 1 SDKs plus npm libraries, and carries zero domain knowledge.
Overview
Xema biome services share one Postgres database and isolate their tables per
biome by schema (biome_<id>). This package makes the correct database
construction the only construction a biome can perform, so biome services carry
no DB boilerplate. It provides:
- The schema-qualified driver-adapter.
createBiomePrismaAdapter(and its…FromEnvsibling) build aPrismaPgpool that passes the schema as the second constructor argument — the one thing that makes Prisma qualifybiome_<id>.<table>instead ofpublic.<table>. Raw queries are covered separately via the libpqsearch_pathoption. - The schema-naming convention.
deriveBiomeSchemaName(biomeId, dbKey?)produces a deterministic, collision-resistant, 63-byte-safe schema name so two biomes never collide. - Config resolution.
resolveBiomeDatabaseConfigFromEnvreads the standardDB_*contract (with per-key suffixes);fetchSystemDatabaseConfigpulls a fully-resolved config from the control plane. - Boot-time bootstrap.
bootstrapBiomeDatabasesresolves config, writes an inline CA cert if supplied, creates the schema under an advisory lock, runsprisma migrate deploy, and exports the resolved connection into the env. - Systematic org-tenant isolation.
createBiomePrismaServicedetects org-scoped models (any model with anorgIdcolumn) from the client's runtime data model and scopes every query on them to the request's org via a Prisma client extension — no hand-writtenwhere: { orgId }per service.
Tenant isolation
Org-scoped access is pushed down into the client so it is systematic instead of
hand-written. Any model declaring the org column (default orgId; configurable
via tenantIsolation.orgField, opt-outs via tenantIsolation.exemptModels) is
detected at client construction and covered by the isolation extension.
Modes
Resolution: tenantIsolation.mode option > XEMA_TENANT_ISOLATION_MODE env
var > default observe. An invalid env value fails the boot — it never
silently weakens the mode.
| Mode | Behavior of the default injected client |
| --- | --- |
| observe (default) | NO query is rewritten. Every violation enforce would reject (org mismatch in a write payload, org-model access with no org context) is logged as a structured warning. |
| enforce | where clauses are AND-ed with { orgId } (unique wheres included — a cross-org findUnique returns null, a cross-org update/delete raises P2025); create payloads are stamped with the org; violations throw a typed TenantIsolationError. |
| off | Passthrough kill switch. forOrg() still enforces — explicit scoping is never silently degraded. |
The rollout story is deliberate: everything ships in observe (zero
behavior change across ~60 services), each service burns down its warnings,
then flips itself to enforce via XEMA_TENANT_ISOLATION_MODE=enforce (or the
module option). enforce is the target state; off exists only as an
emergency kill switch.
Where the org comes from
Inside a request, the org is the RequestContext.orgId that
RequestContextMiddleware resolved. AmbientOrgContextMiddleware
(@xemahq/platform-common) runs right after it and lifts the org into the
ambient async scope the extension reads. Services wired through
configureAuthMiddleware / applyXemaServiceMiddleware get BOTH middlewares
automatically — no per-service wiring is needed.
The boot-path rule
Outside a request (boot, migrations, seeding, contribution sync, Temporal
activities, background workers) there is NO ambient org. In enforce mode,
touching an org-scoped model there throws. Scope explicitly:
// Jobs/workers acting for one org — hard-bound, always enforced:
const scoped = prisma.forOrg(orgId);
await scoped.artifact.findMany(); // implicitly WHERE orgId = <orgId>
// Legitimate cross-org system paths (boot backfills, platform admin):
const system = prisma.asSystem(); // unscoped; debug-logged per call site
// Non-HTTP entrypoints that DO have an org (queue consumers, activities):
// runWithAmbientOrg comes from @xemahq/platform-common.
await runWithAmbientOrg(orgId, () => handler());forOrg(orgId) always enforces, in every mode. asSystem() returns the raw
client and logs each distinct call site once at debug level.
Conformance testing
@xemahq/biome-test-utils exports assertTenantIsolation(client, { orgA,
orgB, models }) — given the wrapped client and a list of org-scoped model
probes, it verifies cross-org reads return empty/null, cross-org writes throw,
and asSystem() sees both orgs, cleaning up after itself.
When to use it
- Use it inside a biome service so the service never hand-wires Prisma, the schema name, or the migrate-time connection string.
- Reach for
createBiomePrismaAdapterFromEnvin aPrismaServiceconstructor, andbootstrapBiomeDatabasesat service boot before Nest starts.
Installation
pnpm add @xemahq/biome-database-nestUsage
import {
bootstrapBiomeDatabases,
createBiomePrismaAdapterFromEnv,
} from '@xemahq/biome-database-nest';
// At boot (main.ts), before NestFactory.create:
await bootstrapBiomeDatabases({
biomeId: 'mailops',
databases: [{ workspaceDir: process.cwd() }],
registry,
serviceToken: () => identity.getAccessToken(),
});
// In PrismaService:
const adapter = createBiomePrismaAdapterFromEnv(process.env);Peer requirements
@prisma/client,@prisma/adapter-pg— the consumer owns the Prisma version; the adapter is constructed against the consumer's copy.
License
Apache-2.0 © Xema — xema.dev
