Getting Started
StatusForge normalizes every error shape your app will ever encounter into a single predictable object. Install it, wrap your catch blocks, and stop writing client-specific error handling.
Installation
npm i @npmforge/statusforge
yarn add @npmforge/statusforge
pnpm add @npmforge/statusforge
Zero dependencies
Basic usage
Import normalizeError and pass any caught value. You always get back the same five fields regardless of what threw.
import { normalizeError } from "@npmforge/statusforge";
try {
const res = await fetch("/api/data");
if (!res.ok) throw res;
} catch (err) {
const { code, message, status, retryable } = normalizeError(err);
showToast(message);
if (retryable) scheduleRetry();
}Output schema
Every call to normalizeError returns a NormalizedError with these fields:
| Field | Type | Description |
|---|---|---|
| code | string | Machine-readable error token for switch/match logic. |
| message | string | Human-readable explanation safe to display in UI. |
| status | number | null | HTTP status code when available; null for transport or non-HTTP errors. |
| retryable | boolean | Whether retrying the request is likely to succeed. |
| details | Record<string, unknown> | null | Original error value preserved for logging and observability. |
Why StatusForge?
Every HTTP client throws differently. Axios wraps errors in AxiosError. Fetch never throws on non-2xx responses. ky throws HTTPError. Apollo returns an errors array. Node transport errors carry codes like ECONNRESET. Without a normalizer, every catch block needs its own branching logic — and it breaks the moment you switch libraries.
Consistent error handling
One function, one output shape. Your UI toasts, retry logic, and logging code stay the same regardless of which client made the request.
Built-in retry intelligence
The retryable flag is inferred from status codes, network error codes, and message patterns so you never have to maintain a list of retryable conditions.
Full observability
The details field preserves the original error for Sentry, Datadog, or any logging pipeline that needs the raw context.
Supported error sources
StatusForge ships six built-in adapters that handle the most common error shapes. See the Adapters reference for detection rules and examples.
Recipes
Retry with exponential backoff
Use the retryable flag to decide whether to retry, without maintaining a list of retryable status codes per client.
import { normalizeError } from "@npmforge/statusforge";
async function fetchWithRetry(url: string, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
const res = await fetch(url);
if (!res.ok) throw res;
return await res.json();
} catch (err) {
const normalized = normalizeError(err);
if (!normalized.retryable || i === attempts - 1) {
throw normalized;
}
await new Promise((r) => setTimeout(r, 1000 * 2 ** i));
}
}
}Structured logging
Pass the normalized error to your logger for consistent, structured error events.
import { normalizeError } from "@npmforge/statusforge";
function logError(err: unknown) {
const { code, message, status, details } = normalizeError(err);
logger.error({
code,
message,
status,
details,
timestamp: Date.now()
});
}