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()