Skip to main content

db-result

Database access with better-result — classify failures into 14 db/* tagged errors, retry only what is safe, run whole transactions, and let the query builder's own type narrow the error union per ORM shape. Use when writing code that touches a SQL driver or ORM (pg, postgres.js, mysql2, mssql, better-sqlite3, node:sqlite, bun:sqlite, libsql, D1, Drizzle, Kysely, Prisma) in a TypeScript project that uses or is adopting better-result — wrapping queries with tryDb, handling unique/foreign-key/connection/deadlock failures, writing ON CONFLICT or upsert logic, choosing retry behavior, starting or joining transactions, or deciding how not-found should be represented.

معلومات المصدر

المستودع
jokull/db-result
آخر نشاط في المصدر
١٠ أغسطس ٢٠٢٦ في ١١:٢٣
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
8 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
db-result
description
Database access with better-result — classify failures into 14 db/* tagged errors, retry only what is safe, run whole transactions, and let the query builder's own type narrow the error union per ORM shape. Use when writing code that touches a SQL driver or ORM (pg, postgres.js, mysql2, mssql, better-sqlite3, node:sqlite, bun:sqlite, libsql, D1, Drizzle, Kysely, Prisma) in a TypeScript project that uses or is adopting better-result — wrapping queries with tryDb, handling unique/foreign-key/connection/deadlock failures, writing ON CONFLICT or upsert logic, choosing retry behavior, starting or joining transactions, or deciding how not-found should be represented.
# db-result `db-result` turns every database failure into a `better-result` tagged error — `Result<T, DbError>` — and makes the retry decision for you, safely. Driver- and ORM-agnostic: it reads the _protocol_ error shape (SQLSTATE, SQLite codes, mysql2 errno, mssql number, Prisma P-codes), never any ORM's API. **This file is a map, not the documentation.** The reference pages in [`references/`](./references/) are the single source of truth; they describe the shipped API, and this file only routes you to them. When a reference conflicts with this file, the reference wins — it is maintained; this map is not. ## Escalation ladder Start at the rung that matches where you are, then follow the links. Don't read everything — each rung loads only what it needs. | Rung | You are here | Read | | ---------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | **What is this** | Never seen db-result or better-result | [`references/overview.md`](./references/overview.md) — the idea in one page | | **0** | Using better-result or not — deciding whether to adopt | [`references/adoption.md`](./references/adoption.md) — the case, the cost, the alternative | | **1** | Installing | [`references/adoption.md#install`](./references/adoption.md#install) — `bun add better-result db-result` + subpath entry points | | **2** | Migrating an existing codebase | [`references/adoption.md#migrate`](./references/adoption.md#migrate) — boundary-first strategy, error classification, fold-at-boundary | | **3** | Writing database code | The task map below — jump to the topic | ## Task map — jump to the topic Fetch the linked reference page for the task at hand: | When you're… | Read | | ------------------------------------------- | --------------------------------------------------------------------------------------------- | | Wrapping any query / statement | [`references/patterns.md`](./references/patterns.md) | | Handling a unique / FK / constraint failure | [`references/vocabulary.md`](./references/vocabulary.md) | | Choosing retry behavior | [`references/retry.md`](./references/retry.md) | | Starting a whole transaction | [`references/transactions.md`](./references/transactions.md) | | Writing a statement _inside_ a transaction | [`references/transactions.md#in-transaction`](./references/transactions.md#in-transaction) | | Narrowing the union from the thunk's shape | [`references/shapes.md`](./references/shapes.md) | | Upsert / `ON CONFLICT` / idempotency | [`references/patterns.md#upsert`](./references/patterns.md#upsert) | | Deciding what a not-found should be | [`references/vocabulary.md#not-found`](./references/vocabulary.md#not-found) | | Adding a driver / protocol shape | [`references/vocabulary.md#adding-a-driver`](./references/vocabulary.md#adding-a-driver) | | "Which subpath for my driver/ORM?" | [`references/adoption.md#install`](./references/adoption.md#install) — the Drizzle driver map | | Observability (tags, cause, retries) | [`references/retry.md#observability`](./references/retry.md#observability) | ## The one rule that is a correctness bug if broken **Attempt the insert — that _is_ the uniqueness check.** Never SELECT-then-INSERT to "check first"; run the write and classify the failure (`db/unique-violation`). It is race-safe where check-then-act is not, and it is what the retry doctrine assumes. ## Non-negotiables - **Wrap with `tryDb`, fold with `matchErrorPartial`.** DB tags are private composition currency — never wire errors. Fold them into domain errors at the handler boundary. - **Pass the query builder, or a thunk.** A builder value (`tryDb(db.selectFrom("users").selectAll())`) is both the retry unit and the shape: the union narrows to what that shape provably cannot raise, and retry re-executes the builder. A thunk (`tryDb(() => prisma.user.findMany(args))`) gets the full union and retry by re-invoking — the form for one-shot calls (Prisma, raw SQL). A settled promise never auto-retries (it can't re-run; a dev warning fires once — wrap in a thunk to get retry). - **The builder's own type is the shape signal.** Nothing to declare, nothing to sync: the ORM emitted the type, so the evidence is verified by construction — a select drops the constraint tags, a delete keeps only FK; `transaction-aborted` is never excluded (tx-bound builders raise 25P02). A builder that proves no shape (raw SQL, Kysely `mergeInto`) is a compile error on purpose (fail-loud, never a silent full union); a builder wrapped in a thunk is a compile error too (pass it directly). See [`references/shapes.md`](./references/shapes.md) — the lattice, the footgun, the honest ceilings. - **Retrying is classified, not guessed.** Deterministic errors (constraints, auth, data) and ambiguous outcomes (connection lost mid-query, unknown commit outcome) are never auto-retried. The transient set is small and per-error. - **Strip `cause` before any wire boundary.** `TaggedError.toJSON()` spreads `cause` (with stack) by design.
عرض على GitHub