AsyncHandler
The AsyncHandler provides a small, explicit execution boundary for running synchronous or asynchronous functions while guaranteeing error normalization.
It is intentionally not middleware and is never applied automatically.
Purpose
AsyncHandler exists to solve one problem:
Ensure that anything thrown beyond a defined execution boundary is always an
AppError.
It provides a safe, explicit way to execute logic without allowing arbitrary error shapes to leak across layers.
What AsyncHandler Is
- A boundary utility for execution
- A failure normalization guard
- Explicit and opt-in
- Framework-agnostic
What AsyncHandler Is Not
AsyncHandler is not:
- Middleware
- A transport adapter
- A global error handler
- A replacement for UI error boundaries
- A retry or orchestration mechanism
It does not intercept requests, inject context, or manage control flow beyond error normalization.
API
AsyncHandler.run
Executes an asynchronous function and normalizes any thrown error.
AsyncHandler.run<T, A extends unknown[]>(
fn: (...args: A) => Promise<T>,
args: A,
fallbackCode?: ErrorCode
): Promise<T>
Behavior
- Executes
fn(...args) - Returns the resolved value on success
- Catches any thrown value
- Throws a normalized
AppErrorusing the provided fallback error code
AsyncHandler.sync
Executes a synchronous function and normalizes any thrown error.
AsyncHandler.sync<T, A extends unknown[]>(
fn: (...args: A) => T,
args: A,
fallbackCode?: ErrorCode
): T
Behavior
- Executes
fn(...args) - Returns the result on success
- Catches any thrown value
- Throws a normalized
AppError
Error Normalization
AsyncHandler guarantees that:
- Only
AppErrorescapes the execution boundary unknownerrors are never propagated- Existing
AppErrorinstances are preserved - All other errors are normalized via
AppError.from
This makes downstream handling deterministic and safe.
Usage Guidelines
Allowed Usage
AsyncHandler may be used in:
- services
- domain logic
- request handlers
- background jobs
- integration layers
Anywhere explicit execution boundaries are required.
Disallowed Usage
AsyncHandler must not be used:
- inside UI components
- as a global safety net
- as a substitute for request middleware
- to catch rendering errors
- to suppress or mask failures
Design Principles
Explicit Boundaries
AsyncHandler must be called explicitly.
There is no global interception.
This ensures:
- clear ownership
- predictable behavior
- no hidden control flow
Single Responsibility
AsyncHandler handles only execution and error normalization.
It does not:
- log
- retry
- transform data
- infer context
- attach metadata
Those responsibilities belong elsewhere.
Relationship to Other Error Constructs
AppError
AsyncHandleralways throwsAppError- It does not create custom error logic
- Error semantics are defined exclusively by
AppErrorandErrorMap
Transport Error Adapters
- Transports adapt
AppErrorto protocol-specific responses AsyncHandlerruns before transport adaptation- It is transport-agnostic
UI Error Boundaries
- UI boundaries catch unhandled errors during rendering
AsyncHandlerdoes not replace UI boundaries- They operate at different layers
Summary
AsyncHandler is a small but critical boundary utility.
By normalizing errors early and explicitly, it:
- prevents error-shape leakage
- enforces architectural consistency
- keeps error handling boring and predictable
If you find yourself needing global interception or implicit behavior, you are solving a different problem.