AsyncHandler

Prev Next

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 AppError using 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 AppError escapes the execution boundary
  • unknown errors are never propagated
  • Existing AppError instances 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

  • AsyncHandler always throws AppError
  • It does not create custom error logic
  • Error semantics are defined exclusively by AppError and ErrorMap

Transport Error Adapters

  • Transports adapt AppError to protocol-specific responses
  • AsyncHandler runs before transport adaptation
  • It is transport-agnostic

UI Error Boundaries

  • UI boundaries catch unhandled errors during rendering
  • AsyncHandler does 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.