@harshilrajput/universal-api-errors-core
v0.1.2
Published
Framework-agnostic core of universal-api-errors: one normalized error object no matter where the error came from.
Maintainers
Readme
@harshilrajput/universal-api-errors-core
The framework-agnostic core of universal-api-errors:
one normalized UniversalError no matter which HTTP client threw it. Zero runtime dependencies.
Install
npm install @harshilrajput/universal-api-errors-core
# or: pnpm add @harshilrajput/universal-api-errors-core
# or: yarn add @harshilrajput/universal-api-errors-coreUsage
import { parseError } from '@harshilrajput/universal-api-errors-core';
try {
await doSomething();
} catch (err) {
const error = parseError(err);
error.message; // string, always present
error.status; // number | undefined
error.code; // string | undefined
error.type; // "Network" | "Timeout" | "Unauthorized" | "Forbidden" | "NotFound"
// | "Validation" | "RateLimit" | "Server" | "Cancelled" | "Unknown"
error.retryable; // boolean
error.validation; // { [field: string]: string[] } | undefined
error.source; // "axios" | "fetch" | "node" | "unknown" | ...
error.isUnauthorized;
error.isForbidden;
error.isNotFound;
error.isRateLimited;
error.isServer;
error.isNetwork;
error.isOffline;
error.isTimeout;
error.isCancelled;
error.isDNS;
error.isSSL;
error.isCors;
error.shouldRetry(); // uses `retryable`, or pass a custom predicate
error.debug(); // pretty console block
error.log(); // structured log, redacts secrets by default
}parseError works without any adapter installed — it recognizes the { response: { status, data } }
shape used by Axios, ky, and superagent, Node error codes (ECONNREFUSED, ETIMEDOUT, ...), and
browser fetch's network-failure message family, all via duck-typing. Install
@harshilrajput/universal-api-errors-axios or
@harshilrajput/universal-api-errors-fetch for
client-specific nuance (e.g. reading a fetch Response body, which requires an await).
Retry helper
import { retry } from '@harshilrajput/universal-api-errors-core';
const user = await retry(() => fetchUser(id), {
retries: 3,
delay: 'exponential', // or "fixed", or a number of ms
jitter: true, // default: true — full jitter to avoid thundering herds
});Validation normalization
import { normalizeValidation } from '@harshilrajput/universal-api-errors-core';
normalizeValidation({ email: ['Already exists'] }); // Laravel/Rails
normalizeValidation([{ path: 'email', msg: 'Already exists' }]); // express-validator
normalizeValidation([{ loc: ['body', 'email'], msg: 'field required' }]); // FastAPI
// => { email: ["Already exists" | "field required"] } in every caseLogging
import { configureLogger } from '@harshilrajput/universal-api-errors-core';
configureLogger({
redactKeys: ['password', 'token', 'ssn'], // extends the built-in defaults
format: 'json', // force json instead of auto (pretty outside NODE_ENV=production)
sink: (payload) => myLogger.error(payload), // route to pino/winston/etc. instead of console
});See the monorepo README for the full project vision and architecture notes.
License
MIT
