Project not found.
scopedDb helper: westside-admin
scopedDb helper: westside-admin
Sub-component focus on the
scopedDb / systemDb query layer. Sits inside arch-dataflow-westside-admin as the Postgres access seam — every server route reads/writes through this helper, never directly through the unscoped Drizzle db.Diagram
Components
| Component | arch: label | Purpose | Notes |
|---|---|---|---|
| <code>scopedDb</code> | <code>arch:scoped-db</code> | Tenant-scoped query builder. Auto-injects <code>WHERE tenant_id = TENANT_ID</code> for every <code>select</code>/<code>update</code>/<code>insert</code>. | The ONLY exported DB entry point for tenant-scoped tables. Lives in <code>src/lib/server/scopedDb.ts</code>. |
| <code>systemDb</code> | <code>arch:scoped-db</code> | Unscoped builder for system tables that have no <code>tenant_id</code> column. | Compile-time error if used on a tenant-scoped table. Tables: <code>alembic_version</code>, <code>player_teams</code>. |
| <code>db</code> (internal) | — | Raw Drizzle client. | Marked <code>@internal</code>; only <code>scopedDb.ts</code> may import. Lint rule + CI grep enforce single entry point. |
| <code>tenant.ts</code> | — | Source of <code>TENANT_ID</code>. | v1: hardcoded constant <code>1</code>. Documented swap point for multi-tenant future (read from JWT claim instead). |
| Drizzle <code>schema.ts</code> | — | Type information consumed by both helpers. | Generated by <code>drizzle-kit pull</code>. The TS types tell <code>scopedDb</code> which tables have <code>tenant_id</code>. |
Key Decisions
- Single entry point, lint-enforced. Any
import { db } from '$lib/server/db'outsidescopedDb.tsfails CI. Prevents the "forgot the WHERE clause" class of bug at the type system level rather than at code review level. - Type system distinguishes tenant tables from system tables. Misusing
scopedDb.select(systemTable)orsystemDb.select(tenantTable)is a TypeScript error, not a runtime guard. Drizzle's column metadata makes this provable at compile time. - tenant_id sourced from constant, not JWT. v1 has one tenant. The constant + helper makes the future swap to claim-sourced trivial — change one file, not 50 call sites.
- Helper composes with arbitrary
where/and/or. Tenant scoping is added viaand(eq(tenant_id, TENANT_ID), ...userClauses)— not a string concat. Drizzle's typed builders make this safe. - This is a deliberate sub-component. The helper is logically part of the dataflow described in
arch-dataflow-westside-admin, but its surface area (two exports, one lint rule) is large enough to warrant its own arch note. Tickets touching this layer carryarch:scoped-db.
Implementation Tickets
forgejo_admin/westside-admin#1(board #1089) — creates the unscopeddb, generatesschema.tsforgejo_admin/westside-admin#3(board #1091) — createsscopedDb+systemDb+ lint rule +tenant.ts
Related
arch-dataflow-westside-admin— parent dataflow note (this is the DB-access seam within it)arch-domain-westside-admin— table inventory; tenant_id presence drives scope decisionsproject-westside-admin— project pagestory-westside-admin-admin-row-crud— driving user story (this helper is a Safety Constraint guarantee)convention-architecture-ids— labeling convention