3.6 KiB
Error codes
Purpose
This module defines the stable, namespaced error-code registry for
@suaveplan/api-plugin-banking. Every failure path in the plugin throws
a BankingError carrying one of fourteen BANKING.* codes, so callers
can branch on error.code without parsing free-form messages and the
HTTP layer can map a code → status with a one-line lookup.
Features
- 14 typed string-literal codes registered via
defineCodes("BANKING", ...) BankingErrorclass extendingSuaveplanError, generic over the code literal type soinstanceofnarrows thecodefield- Stable JSDoc with HTTP-status mapping for each code
- Re-exported under the package's
./error-codes/error-codessubpath
Usage: Basic
import { BankingError, BANKING_CODES } from "@suaveplan/api-plugin-banking";
throw new BankingError(
BANKING_CODES.ACCOUNT_NOT_FOUND,
`Account ${id} not found`
);
Usage: Advanced
Codes carry no behaviour — they are documentation. The expected pattern
is to catch at the route boundary, narrow on instanceof BankingError,
and map error.code to an HTTP status:
import {
BankingError,
BANKING_CODES,
type BankingErrorCode,
} from "@suaveplan/api-plugin-banking";
const STATUS: Record<BankingErrorCode, number> = {
INVALID_OPTIONS: 500,
SERVICE_NOT_AVAILABLE: 500,
BANK_NOT_FOUND: 404,
BANK_NAME_TAKEN: 400,
BANK_HAS_ACTIVE_ACCOUNTS: 400,
ACCOUNT_NOT_FOUND: 404,
ACCOUNT_LIMIT_REACHED: 400,
INSUFFICIENT_FUNDS: 400,
INVALID_AMOUNT: 400,
INVALID_FIELD_NAME: 500,
INVALID_ACCOUNT_NUMBER: 400,
INVALID_SORT_CODE: 400,
TENANT_CLEANUP_FAILED: 500,
DATABASE_ERROR: 500,
};
try {
await service.transferFunds(from, to, amount);
} catch (err) {
if (err instanceof BankingError) {
const status = STATUS[err.code];
return { status, body: { error: err.message, code: err.code } };
}
throw err;
}
API Reference
BANKING_CODES
const BANKING_CODES: { readonly INVALID_OPTIONS: "BANKING.INVALID_OPTIONS" } & {...}
Frozen, namespaced map of every code in the registry. Indexing it
yields the canonical "BANKING.XYZ" string passed to BankingError.
BankingErrorCode
type BankingErrorCode = (typeof BANKING_CODES)[keyof typeof BANKING_CODES]
Discriminated union of all literal code values.
class BankingError extends SuaveplanError<BankingErrorCode>
constructor(code: BankingErrorCode, message: string, options?: SuaveplanErrorOptions)
error.code: BankingErrorCode — the literal code string.
error.bankingCode: BankingErrorCode — alias for ergonomic exhaustive
switches without re-typing the union annotation.
error.cause, error.context, error.toJSON() — inherited from
SuaveplanError.
Implementation Notes
The registry is intentionally exhaustive: every error code that appears here is thrown by at least one production code path inside the package. The pre-archive checklist (AGENTS.md §13) gates this — codes registered but never thrown are removed before the change ships, so the list reflects the real fault surface a caller can branch on.
BankingError keeps the bankingCode alias so consumers writing
exhaustive switches do not need to re-type the union annotation:
switch (err.bankingCode) { case "BANKING.ACCOUNT_NOT_FOUND": ... }
type-checks against BankingErrorCode directly.
When throwing from a SurrealDB call site, always set
{ cause: originalError, context: { ... } } — the wire-format JSON
adapter (SuaveplanError.toJSON) preserves both. DATABASE_ERROR is
the catch-all wrapper used by repositories when SurrealDB rejects a
query for a reason outside the typed code list.