Build on the supported package contract
Use @absolutejs/errors-postgres through its supported public entry points.
@absolutejs/errors-postgresv0.2.0-beta.0betaObservabilityPostgres-backed, Effect-native IssueStore for @absolutejs/errors with first-class Drizzle and tagged-template adapters.
bun add @absolutejs/errors-postgresPostgres-backed, Effect-native IssueStore for @absolutejs/errors — the durable "Issues" surface (Sentry's product core), self-hosted on your own Postgres.
Durable grouped issues + an append-only event timeline, with new vs. regression detection resolved in a single atomic CTE upsert — one round-trip, no transaction, so it works over Neon's HTTP driver too.
Drizzle
Re-export the package tables from your application schema and manage them with your normal migration workflow:
The Drizzle store uses transactions for atomic event/group updates, portable native JSONB for tags and extra context, row locking for regression detection, and the same Effect error channel as the tagged-template adapter. JSONB values are passed to Drizzle drivers as native objects (rather than pre-serialized strings), preventing Bun SQL from storing them as JSON strings.
Tagged-template compatibility
Works identically with @neondatabase/serverless:
Every method returns an Effect with a typed IssueStoreError channel (IssueStoreSchemaError / IssueStoreQueryError / IssueStoreSerializationError) — failures are values, not throws.
Method — Effect
record(event) — atomic upsert (one event) → { issue, isNew, isRegression }
recordCoalesced(group) — count-aware upsert + bulk unnest insert — one round-trip per herd
listIssues(filter?) — dashboard list (project / environment / state / ILIKE title), newest-first
getIssue(project, fingerprint) — Option<IssueRecord>
setState(project, fingerprint, state) — resolve / ignore / unresolve
assign(project, fingerprint, who \ — null) — triage
listEvents(project, fingerprint, limit?) — occurrence timeline, newest-first
Grouping semantics (mirrors createMemoryIssueStore exactly)
new vs. regression — detected in one statement: xmax = 0 ⇒ the row
was inserted (new); a CTE captures the pre-update state, so a resolved issue seen again is a regression and flips back to unresolved. ignored issues stay muted.
severity escalates, never de-escalates — fatal > error > warning > info.
Created on first use (set ensureSchema: false to manage it via migrations). tablePrefix defaults to error → error_issues + error_events.
Indexes: (project, last_seen DESC) and (project, state) on issues; (project, fingerprint, at DESC) on events.
trace_id / span_id (→ @absolutejs/telemetry) and replay_id (→ @absolutejs/replay) are stored per event so a dashboard can cross-link an issue to its exact trace and DOM replay.
error_issues also carries the resolution trail — resolved_at, resolved_by, resolved_release, resolution_note — which the triage board writes when an issue is closed. ensureSchema adds them to an existing table; an application that owns its schema through migrations must add them itself before upgrading, because the store writes every column it knows about.
Choose the schema-derived Drizzle store for application-managed databases or the raw tagged-template compatibility store for lightweight and Neon HTTP integrations.
The store answers "what broke and how often". A board on top of it has to answer what a person actually asks in front of it: what is breaking now, who is on it, did the fix hold, which release introduced this. createIssueTriage is those queries, bound to one project.
A regression is derived, not stored: the ingest upsert flips a resolved issue back to unresolved when it recurs but leaves resolved_at in place, so unresolved + a resolution stamp + activity after it means the fix did not hold. setState clears the trail on reopen, because a manual reopen is a decision rather than a failed fix — and a surviving stamp would make the next recurrence look like one.
releaseStats is the per-release rollup a deployments ledger needs: for each release, how many issues were introduced under it, carried in from before it, came back in it, or were declared fixed in it — split by severity, because a deploy that introduced three warnings is not the same news as one that introduced three errors. Each issue lands in exactly one bucket per release.
Outcomes
Use @absolutejs/errors-postgres through its supported public entry points.
Hardening checklist
Follow in order
Re-export the package tables from your application schema and manage them with your normal migration workflow:
export { errorEvents, errorIssues } from "@absolutejs/errors-postgres";Re-export the package tables from your application schema and manage them with your normal migration workflow:
import { createDrizzleIssueStore } from "@absolutejs/errors-postgres";
const store = createDrizzleIssueStore({ db });Supported entry points declared by this package manifest.
Package entry point declared in package.json.
Scripts declared by this package manifest.