Initial Commit
This commit is contained in:
107
packages/persistence/database/db/src/errors/database-error.md
Normal file
107
packages/persistence/database/db/src/errors/database-error.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# DatabaseError / MigrationError
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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`
|
||||
|
||||
```ts
|
||||
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`
|
||||
|
||||
```ts
|
||||
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`
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user