@haatlabs-core/db-core
v0.10.0
Published
PostgreSQL and drizzle building blocks: shared columns, organizations, row-level security, audit logs.
Readme
@haatlabs-core/db-core
What every app's database code shares, on PostgreSQL with drizzle. db-core owns no table and no migration: each app declares its tables and writes its migrations. What it owns is the shared columns, and the code that works on them.
createDatabase(url, { max }): a pool whoseclose()waits for every connection.Databaseis typed without a schema (nothing uses drizzle's relational queries), so one type serves every app.withTenant(db, ctx, fn): a transaction withapp.organization_id(TENANT_SETTING) bound for row-level security. Each app's policies read that setting.runMigrations(systemUrl, { migrationsFolder }),runMigrationsCli(...).@haatlabs-core/db-core/testing:startTestDatabase({ name, migrationsFolder, afterMigrate }), a migrated PostgreSQL in a container (needs@testcontainers/postgresql).Column sets for the shared tables, and their indexes, from which an app builds its own:
export const organizations = pgTable('organizations', { ...organizationColumns(), storeLimit: integer('store_limit'), // the app's own: nullable or with a default }); export const memberships = pgTable('memberships', membershipColumns({ users, organizations }), (t) => membershipIndexes(t)); export { organizationOriginEnum, roleEnum } from '@haatlabs-core/db-core'; // for drizzle-kitRepositories that take the app's tables:
IdentityRepository,ApiKeyRepository,WebhookEventRepository,OutboxRepository(priority: the app's column, orfalse),PlatformAdminRepository. A table missing a shared column does not compile where the repository is built.createOutboxWriter,createAuditWriter,isUniqueViolation.
drizzle-orm and pg are peers: the app's own copies are used.
Billing columns (since 0.5.0): planColumns, planFeatureColumns, planPriceColumns,
subscriptionColumns, paymentEventColumns, usageRecordColumns,
organizationFeatureColumns, each with its indexes; organizations.store_limit; and the
outbox's outboxPriorityColumn(), whose default organization_goes_first() an app's
migration creates from ORGANIZATION_GOES_FIRST_SQL before the outbox table.
insertOutbox.many(tx, messages) writes many outbox rows, one INSERT per 500, in the caller's
transaction. An outbox row carries no delayMs and no deduplication (@haatlabs-core/queue), so a
publish that needs them is made directly, after the commit, and is lost if the process dies
between the two. A caller that needs the message to survive writes it to the outbox, without
them, and coalesces in the handler instead.
Install
pnpm add @haatlabs-core/db-coreIt leaves these packages to your app, so the app holds one copy: drizzle-orm, pg, @testcontainers/postgresql.
