AvelonDocs

@avelon

postgres

@avelon/postgres

@avelon/postgres is the Postgres database driver for Avelon. It compiles the frozen QueryIR into parameterized SQL, runs it through Bun's SQL client, and maps vendor failures into the framework error taxonomy. Reach for this package when your application needs transactions, deep relation loads, upserts with returning, or an independent Postgres connection beside Supabase.

Installation

bun add @avelon/postgres

Set a connection URL in the environment or pass one when constructing the driver:

export POSTGRES_URL=postgresql://postgres:avelon@127.0.0.1:5432/avelon

Basic Usage

import { createPostgresDatabase } from '@avelon/postgres'
import type { QueryIR } from '@avelon/core'

const db = createPostgresDatabase({
  url: process.env.POSTGRES_URL,
  instance: 'primary',
})

const latest: QueryIR = {
  table: 'posts',
  mode: 'select',
  select: ['id', 'title'],
  where: [{ kind: 'null', column: 'published_at', negated: true }],
  relations: [],
  order: [{ column: 'published_at', direction: 'desc' }],
  limit: 10,
}

const result = await db.execute<{ id: string; title: string }>(latest)

Capabilities

CapabilityValueNotes
transactionstrueInteractive transactions through db.transaction()
rowSecurityfalseWard-to-RLS compilation belongs to @avelon/supabase
maxRelationDepth8Application-side nested loads; measured against nested assay fixtures
fullTextSearchfalseNo portable search() surface in v1
upserttrueRequires explicit conflict target and update list
returningtrueWrite queries may project rows
windowFunctionstrueInformational; available through raw()
jsonOperatorstrueInformational; available through raw()

Query Compilation

The driver validates IR shape, rejects unknown public-schema identifiers, normalizes predicates, and compiles positional SQL. Empty AND is true, empty OR is false, empty IN matches nothing unless negated, and a constant-false ward or where short-circuits without a database round trip.

import { compilePostgres, combinedPredicate } from '@avelon/postgres'
import type { QueryIR } from '@avelon/core'

const query: QueryIR = {
  table: 'assay_users',
  mode: 'select',
  select: ['id'],
  where: [{ kind: 'compare', column: 'age', op: '>=', value: 18 }],
  ward: { kind: 'compare', column: 'age', op: '<', value: 30 },
  relations: [],
  order: [],
}

const compiled = compilePostgres(query, combinedPredicate(query))
// compiled.text binds age thresholds as $1 and $2

Transactions

import { createPostgresDatabase } from '@avelon/postgres'

const db = createPostgresDatabase()

await db.transaction(async (tx) => {
  await tx.execute({
    table: 'assay_users',
    mode: 'insert',
    select: [],
    where: [],
    relations: [],
    order: [],
    values: { id: 'u1', email: 'one@example.test', name: 'One', age: 20, nickname: null },
  })
})

Migrations

Migrations are driver-owned SQL pairs registered on the driver instance.

import { createPostgresDatabase } from '@avelon/postgres'

const db = createPostgresDatabase({
  migrations: [
    {
      id: '20260804_create_posts',
      up: [
        'CREATE TABLE posts (id text PRIMARY KEY, title text NOT NULL)',
      ],
      down: ['DROP TABLE posts'],
    },
  ],
})

await db.plan()
await db.apply()
await db.status()

Error Mapping

Vendor SQLSTATE values never leave the driver. Unique violations become Conflict, unknown tables/columns/routines become Invalid, and connection failures become Unavailable. Every other code becomes DriverFault.

SQLSTATEFramework errorMeaning
23505Conflictunique_violation
42P01Invalidundefined_table
42703Invalidundefined_column
42883Invalidundefined_function
08006Unavailableconnection_failure

Live Conformance

Fixture provisioning is owned by this package. Reset the assay schema, then run the shared database suite against a live Postgres:

export POSTGRES_URL=postgresql://postgres:avelon@127.0.0.1:5432/avelon_test
bun run fixtures:reset
bun test

Method Reference

MethodSignatureDescription
createPostgresDatabase(options?: PostgresDatabaseOptions) => PostgresDatabaseConstructs a driver from options or POSTGRES_URL / DATABASE_URL.
postgresDatabaseCapabilities{ transactions: true, rowSecurity: false, maxRelationDepth: 8, fullTextSearch: false, upsert: true, returning: true, windowFunctions: true, jsonOperators: true }Exact capability declaration for the Postgres driver.
PostgresDatabaseOptionsinterfaceConnection URL, instance name, and optional migrations.
PostgresDatabase.execute(query: QueryIR) => Promise<QueryResult>Validates, compiles, and executes one query IR operation.
PostgresDatabase.rpc(routine: string, args: Readonly<Record<string, unknown>>) => Promise<T>Invokes a Postgres routine; missing routines raise Invalid.
PostgresDatabase.transaction(callback: (tx: DatabaseTransaction) => Promise<T>) => Promise<T>Runs the callback atomically and returns its result.
PostgresDatabase.plan() => Promise<MigrationPlan>Returns pending migration identifiers and SQL steps.
PostgresDatabase.apply() => Promise<readonly MigrationStatus[]>Applies pending migrations inside transactions.
PostgresDatabase.rollback(steps?: number) => Promise<readonly MigrationStatus[]>Rolls back the newest applied migration batches.
PostgresDatabase.status() => Promise<readonly MigrationStatus[]>Lists applied and pending migration states.
PostgresDatabase.raw() => SQLReturns the Bun SQL client at the vendor boundary.
PostgresDatabase.resetFixtures() => Promise<void>Recreates assay tables and assay_echo for live conformance.
PostgresDatabase.close() => Promise<void>Closes the underlying SQL client pool.
compilePostgres(ir: QueryIR, predicate?: Predicate) => CompiledSqlCompiles IR to parameterized SQL without executing it.
compileSqlPredicate(predicate: Predicate, bind: (value: unknown) => string) => stringCompiles a normalized predicate to a SQL boolean expression.
CompiledSqlinterfaceParameterized text plus positional parameters.
normalizePredicate(predicate: Predicate) => PredicateApplies empty-list and constant identities.
combinedPredicate`(ir: Pick<QueryIR, 'where' \'ward'>) => Predicate`ANDs where and ward, then normalizes.
mapPostgresError(error: unknown, operation: string) => neverMaps vendor failures into framework errors.
POSTGRES_ERROR_MAPreadonly { sqlstate, framework, meaning }[]SQLSTATE values this driver maps into the taxonomy.
validateQueryIR(ir: QueryIR, maxRelationDepth: number) => voidRejects malformed IR before compilation.
assertIdentifier(value: unknown, path: string) => asserts value is stringRejects identifiers that are not simple SQL names.
assertQueryAgainstSchema(cache: SchemaCache, query: QueryIR) => voidRejects unknown public-schema tables and columns.
loadSchemaCache(sql: SQL) => Promise<SchemaCache>Loads public base tables and their columns.
SchemaCachetypeMap of table name to column set.
planMigrations(sql: SQL, migrations: readonly PostgresMigration[]) => Promise<MigrationPlan>Builds a pending plan from registered migrations and history.
applyMigrations(sql: SQL, migrations: readonly PostgresMigration[]) => Promise<readonly MigrationStatus[]>Applies pending migrations inside transactions.
rollbackMigrations(sql: SQL, migrations: readonly PostgresMigration[], steps?: number) => Promise<readonly MigrationStatus[]>Rolls back the newest applied migration batches.
statusMigrations(sql: SQL, migrations: readonly PostgresMigration[]) => Promise<readonly MigrationStatus[]>Returns applied/pending status for every registered migration.
PostgresMigrationinterfaceDriver-owned id, up, and down SQL pair.
resetAssayFixtures(sql: SQL) => Promise<void>Provisions empty assay fixtures on a SQL client.
ASSAY_FIXTURE_SQLstringSQL that drops and recreates the database conformance fixtures.

Testing

Use the shared database conformance suite with a live database. The package ships fixture reset helpers so tests do not rely on hand-maintained schema.

import { databaseSuite } from '@avelon/conformance/suites'
import { createPostgresDatabase } from '@avelon/postgres'

databaseSuite({
  name: 'live postgres',
  create: async () => {
    const driver = createPostgresDatabase()
    await driver.resetFixtures()
    return driver
  },
})

Unit tests cover SQL compilation and error mapping without a network round trip. Live tests require Postgres and fail closed when the database is unreachable.