Jordan Smalls

Software Engineer

Handle fetch errors correctly

Throw on unsuccessful HTTP responses and keep the useful server message.

Fetch only rejects when the request itself fails. A 404 or 500 still resolves, so check response.ok before treating the request as successful.

typescript
export async function fetchJson<T>(
  input: RequestInfo | URL,
  init?: RequestInit,
): Promise<T> {
  const response = await fetch(input, init)

  if (!response.ok) {
    const message = await response.text()
    throw new Error(message || `Request failed with status ${response.status}`)
  }

  return response.json() as Promise<T>
}

Handle the error where you can show a useful fallback or message. Passing an AbortSignal through init also lets the caller cancel the request when it is no longer needed.

typescript
const controller = new AbortController()

try {
  const user = await fetchJson<User>('/api/user', {
    signal: controller.signal,
  })
} catch (error) {
  if (error instanceof Error && error.name !== 'AbortError') {
    console.error(error.message)
  }
}

// Call this when the result is no longer needed.
controller.abort()