/fetch

@thourum/fault/fetch turns native fetch failures, HTTP errors, and JSON parse failures into ResultAsync values.

safeFetch

safeFetch(input: URL | string, init?: RequestInit): ResultAsync<Response, Fault>

const result = await safeFetch('/api/user')
result.match(handleResponse, showFault)

A successful response returns the native Response with its body untouched. Read it as text, bytes, a stream or JSON as needed. Non-OK responses use the status table below. Reading the returned response body yourself may still throw; use safeFetchJSON when you need JSON parsing in the Result.

safeFetchJSON

safeFetchJSON<T = unknown>(input: URL | string, init?: RequestInit): ResultAsync<T, Fault>

const user = await safeFetchJSON<unknown>('/api/user')
  .andThen((data) => safeZodParse(userSchema, data))

Built on safeFetch. A successful response is always parsed as JSON, regardless of content type. Empty bodies (including 204) and malformed JSON produce PARSE_ERROR; the latter preserves the SyntaxError as its cause. Parse faults retain httpStatus metadata. A request or body read that times out produces TIMEOUT_ERROR with message "The request timed out."; one that is aborted produces ABORTED with message "The request was aborted."; other network and body-read failures produce NETWORK_ERROR.

Tags produced: TIMEOUT_ERROR, ABORTED, NETWORK_ERROR, PARSE_ERROR (JSON helper), BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, RATE_LIMITED, or INTERNAL_ERROR.

HTTP statusTag
400BAD_REQUEST
401UNAUTHORIZED
403FORBIDDEN
404NOT_FOUND
429RATE_LIMITED
500 and aboveINTERNAL_ERROR
Every other non-OK statusBAD_REQUEST

HTTP faults include httpStatus, httpStatusText, httpHeaders, and httpBody metadata. Headers are copied verbatim, so redact sensitive metadata before sending it to logs or telemetry. The body is parsed as JSON when possible and otherwise kept as text.