Skip to content

typedFetch

Type-safe wrapper around the native fetch API that returns errors as values instead of throwing exceptions.

Signature

typescript
function typedFetch<
  JsonReturnType,
  ErrorType extends ClientErrors = ClientErrors,
>(
  url: Parameters<typeof fetch>[0],
  options?: Parameters<typeof fetch>[1] & {
    headers?: TypedHeaders;
    method?: HttpMethods;
  },
): Promise<
  | { response: TypedResponse<JsonReturnType>; error: null }
  | { response: null; error: ErrorType | ServerErrors | NetworkError }
>

Type Parameters

ParameterDescription
JsonReturnTypeExpected response body type on success
ErrorTypeSpecific client error types to expect (defaults to all 4xx). Server errors (5xx) and NetworkError are always included.

Parameters

ParameterTypeDescription
urlstring | URL | RequestThe resource URL, same as fetch()
optionsRequestInitRequest options with typed headers and method (optional, defaults to {})

Return Type

A discriminated union — check error to narrow:

typescript
const { response, error } = await typedFetch<User>('/api/users/1');

if (error) {
  // response is null, error is ErrorType | ServerErrors | NetworkError
} else {
  // error is null, response is TypedResponse<User>
  const user = await response.json(); // Type: User
}

Fetch API Compatibility

typedFetch has the same API as native fetch:

  • All fetch options work: method, headers, body, credentials, cache, etc.
  • Full AbortController support via signal
  • Request timeout using AbortSignal.timeout()
  • Same parameter types as native fetch
  • Drop-in replacement for existing fetch calls

isHttpError

typescript
function isHttpError(error: unknown): error is BaseHttpError

Type guard that checks whether a value is an HTTP error (any BaseHttpError subclass). Prefer this over instanceof when the error may cross package boundaries.

typescript
import { isHttpError } from '@pbpeterson/typed-fetch';

if (isHttpError(error)) {
  console.log(error.status, error.statusText, error.name);
}

isNetworkError

typescript
function isNetworkError(error: unknown): error is NetworkError

Type guard that checks whether a value is a NetworkError.

typescript
import { isNetworkError } from '@pbpeterson/typed-fetch';

if (isNetworkError(error)) {
  console.log('Connection failed:', error.message);
}

statusCodeErrorMap

typescript
const statusCodeErrorMap: Map<number, HttpErrors>

Maps HTTP status codes (400–511) to their corresponding error class constructors.

typescript
import { statusCodeErrorMap } from '@pbpeterson/typed-fetch';

const ErrorClass = statusCodeErrorMap.get(404); // NotFoundError

httpErrors

typescript
const httpErrors: readonly [BadGatewayError, BadRequestError, ...]

Array of all 40 HTTP error class constructors. Useful for iteration and custom registries.

typescript
import { httpErrors } from '@pbpeterson/typed-fetch';

for (const ErrorClass of httpErrors) {
  console.log(`${ErrorClass.status}: ${ErrorClass.statusText}`);
}

Examples

Request Cancellation

typescript
const controller = new AbortController();

const { response, error } = await typedFetch<User[]>('/api/users', {
  signal: controller.signal,
});

controller.abort();

Request Timeout

typescript
const { response, error } = await typedFetch<User[]>('/api/users', {
  signal: AbortSignal.timeout(5000),
});

POST with Typed Headers

typescript
const { response, error } = await typedFetch<User>('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token',
  },
  body: JSON.stringify(userData),
  credentials: 'include',
});

Released under the MIT License.