Typed fetch helper that checks the response
A fetch wrapper that throws on HTTP errors and validates the JSON body with a type guard.
- TypeScript
- GreenSnippets
- MIT
- Executed with Node.js 22 (type stripping)
class HttpError extends Error {
readonly status: number;
constructor(status: number, message: string) {
super(message);
this.status = status;
}
}
async function fetchJson<T>(
url: string,
guard: (value: unknown) => value is T,
init?: RequestInit,
): Promise<T> {
const response = await fetch(url, { ...init, headers: { Accept: "application/json", ...init?.headers } });
if (!response.ok) throw new HttpError(response.status, `${response.status} ${response.statusText}`);
const data: unknown = await response.json();
if (!guard(data)) throw new Error(`Unexpected response shape from ${url}`);
return data;
}Example / expected output
const user = await fetchJson("/api/me", (v): v is { id: string } => typeof (v as any)?.id === "string");How it works
response.json() is typed as any, so a changed API silently breaks your code later. This helper throws a descriptive error for non-2xx responses and verifies the body with a guard before returning it typed. Combine it with a timeout (see the fetch-with-timeout snippet) for production use.
Related snippets
- PHP Tested
Retry a callable with exponential backoff in PHP
Re-run flaky operations with growing delays and jitter, only for chosen exception types.
- JavaScript Tested
Verify a webhook signature in Node.js
Validate an HMAC-SHA256 webhook against the raw request body with a constant-time comparison and replay window.
- PHP Tested
Decode JSON safely in PHP
Parse JSON with exceptions on error and a depth limit, instead of silently getting null.
- JavaScript Tested
Retry an async operation with exponential backoff
Retry a failing promise-returning function with growing, jittered delays and a retry predicate.

