Skip to content

API ReferenceHTTPError

HTTPError

Custom error class for HTTP responses with typed data, status code, and headers. Includes the isHTTPError type guard.

2 min readboth modes

Import

ts
import { HTTPError, isHTTPError } from "@noverax/courier"

HTTPError Class

ts
import { CourierError, type HttpResponse } from "@noverax/courier"

declare class HTTPError<TData = unknown> extends CourierError<"HTTP_ERROR"> {
  readonly status: number
  readonly headers: Headers
  readonly data: TData

  constructor(response: HttpResponse<TData>, options?: { cause?: unknown; message?: string })
}

Properties

ts
error.name       // "HTTPError"
error.code       // "HTTP_ERROR"
error.message    // "HTTP {status}" (e.g., "HTTP 404")
error.status     // HTTP status code (e.g., 404)
error.headers    // Response headers (Headers object)
error.data       // Parsed response body (typed as R)
  • status — the HTTP status code. 0 for network errors (DNS failure, connection refused).
  • headers — the response Headers object. Empty Headers for network errors.
  • data — the parsed response body. Typed as TData (defaults to unknown).
  • name — always "HTTPError"; code — always "HTTP_ERROR".
  • message — "HTTP {status}" (e.g., "HTTP 404"), or, when there was no answer, "Network request failed" or "Request timed out".

isHTTPError

Type guard function to check if an error is an HTTPError:

ts
import type { HTTPError } from "@noverax/courier"

declare function isHTTPError<TData = unknown>(error: unknown): error is HTTPError<TData>

isHTTPError types the error's data

isHTTPError(error) checks what error instanceof HTTPError checks, and takes a type parameter for the error's data: isHTTPError<ErrorResponse>(error) — see Typed Error Data.

Basic Usage

ts
import { isHTTPError } from "@noverax/courier"

try {
  const user = await caller.users.getById({ id: "999" })
} catch (error) {
  if (isHTTPError(error)) {
    console.error(`HTTP ${error.status}:`, error.data)
  }
}

Typed Error Data

Pass a type parameter to isHTTPError to type the error's data:

ts
import { isHTTPError } from "@noverax/courier"

type ErrorResponse = {
  success: false
  message: string
  code?: string
}

try {
  await caller.users.getById({ id: "999" })
} catch (error) {
  if (isHTTPError<ErrorResponse>(error)) {
    console.error(error.data.message)  // "User not found"
    console.error(error.data.code)     // "USER_NOT_FOUND"
  }
}

Status Code Handling

ts
import { isHTTPError } from "@noverax/courier"

try {
  await caller.users.getAll()
} catch (error) {
  if (isHTTPError(error)) {
    switch (error.status) {
      case 400: console.error("Bad request:", error.data); break
      case 401: redirectToLogin(); break
      case 403: showForbiddenMessage(); break
      case 404: showNotFound(); break
      case 429: console.warn("Rate limited, retry later"); break
      default:
        if (error.status >= 500) console.error("Server error:", error.data)
    }
  }
}

Middleware-Based Error Handling

Use middleware to handle errors globally across all requests:

ts
import { defineMiddleware } from "@noverax/courier/middleware"
import { isHTTPError } from "@noverax/courier"

const errorMiddleware = defineMiddleware("errors", "wrap", async (ctx, next) => {
  try {
    return await next()
  } catch (error) {
    if (isHTTPError(error) && error.status === 401) {
      // redirect to login, refresh token, etc.
    }
    throw error
  }
})

Network Errors

Network-level errors (DNS failures, connection refused, timeouts) are converted to HTTPError with status: 0. A timeout says so: its message is "Request timed out" and error.data.code is "TIMEOUT". Through the proxy, a timeout on the server's side arrives as a 504 with code: "UPSTREAM_TIMEOUT":

ts
import { isHTTPError } from "@noverax/courier"

try {
  await caller.users.getAll()
} catch (error) {
  if (isHTTPError(error) && error.status === 0) {
    console.error("Network error:", error.data)
  }
}