Files
2026-05-27 23:47:53 +01:00

4.0 KiB

DatabaseError / MigrationError

(no submodules)

Purpose

DatabaseError is the structured error class for all database failures in @suaveplan/db. It extends SuaveplanError from @suaveplan/error, ensuring every database error carries a stable, machine-readable DB.* code alongside a human-readable message and optional cause chain. MigrationError is a specialisation that records which named migration failed.

Using SuaveplanError as the base means consumers can perform type-safe instanceof checks, extract OTel-compatible codes from error.code, and inspect cause chains consistently across the entire Suaveplan project.

Features

  • Extends SuaveplanError<DbCode> — participates in the project-wide error hierarchy
  • Stable DB.* codes registered via defineCodes — never change between versions
  • cause threading — wraps lower-level driver errors without losing the original stack
  • MigrationError.migration field — names the exact migration file that failed
  • name property set to the concrete class name for easy identification in logs

Usage: Basic Example

import { DatabaseError, MigrationError, DB_CODES } from "@suaveplan/db";

// Throw a connection failure
throw new DatabaseError(
  DB_CODES.CONNECTION_FAILED,
  "Could not reach postgres://localhost:5432/app"
);

// Throw a migration failure
throw new MigrationError("003_add_index", "duplicate column name: email");

Usage: Advanced Example

import { DatabaseError, DB_CODES } from "@suaveplan/db";
import { SuaveplanError } from "@suaveplan/error";

async function runQuery(sql: string): Promise<unknown[]> {
  try {
    return await driver.query(sql);
  } catch (cause) {
    throw new DatabaseError(
      DB_CODES.QUERY_FAILED,
      `Query failed: ${cause instanceof Error ? cause.message : String(cause)}`,
      { cause: cause instanceof Error ? cause : undefined }
    );
  }
}

// Narrowing in catch blocks
try {
  await runQuery("SELECT * FROM users");
} catch (err) {
  if (err instanceof DatabaseError) {
    console.error(err.code, err.message); // "DB.QUERY_FAILED Query failed: ..."
  }
}

API Reference

DatabaseError

class DatabaseError extends SuaveplanError<DbCode> {
  constructor(code: DbCode, message: string, options?: SuaveplanErrorOptions): void
}
  • code (DbCode) — one of DB.CONNECTION_FAILED, DB.NOT_CONNECTED, DB.QUERY_FAILED, DB.TRANSACTION_FAILED, DB.MIGRATION_FAILED
  • message (string) — human-readable description
  • options.cause (Error | undefined) — lower-level error that triggered this one

MigrationError

class MigrationError extends DatabaseError {
  readonly migration: string;
  constructor(migration: string, detail: string, options?: SuaveplanErrorOptions): void
}
  • migration (string) — the migration name (e.g. "001_init")
  • detail (string) — description of what went wrong
  • Message format: "Migration '<migration>' failed: <detail>"
  • Code is always DB.MIGRATION_FAILED

DB_CODES

const DB_CODES: {
  readonly CONNECTION_FAILED: "DB.CONNECTION_FAILED";
  readonly NOT_CONNECTED:     "DB.NOT_CONNECTED";
  readonly QUERY_FAILED:      "DB.QUERY_FAILED";
  readonly TRANSACTION_FAILED:"DB.TRANSACTION_FAILED";
  readonly MIGRATION_FAILED:  "DB.MIGRATION_FAILED";
}

Implementation Notes

Both classes pass the message string directly to SuaveplanError as a literal message key rather than an i18n key, so no message-resolver configuration is required. This keeps @suaveplan/db dependency-free of any i18n infrastructure. If localisation is needed in a future version, the message key can be replaced with an i18n key and the context option used for interpolation.

MigrationError explicitly sets this.name = "MigrationError" in the constructor because SuaveplanError sets this.name = new.target.name — the explicit assignment in the subclass constructor ensures the name is accurate when the class is transpiled or minified.