Skip to content

fetchExpect

fetchExpect is a thin wrapper around native fetch that handles JSON parsing and status validation using expect-status.

import { fetchExpect } from "expect-status/fetch";
type ItemResponse =
| { status: 200; body: { id: string; name: string } }
| { status: 404; body: { message: string } };
const item = await fetchExpect<ItemResponse>(
"https://api.example.com/items/1",
200,
);
// ^? { id: string; name: string }
fetchExpect<R, S>(
url: string | URL,
successStatus: S,
options?: FetchExpectOptions
): Promise<unknown>;

The URL to fetch. Can be a string or URL object.

await fetchExpect("https://api.example.com/items/1", 200);
await fetchExpect(new URL("/items/1", "https://api.example.com"), 200);

The success status or array of success statuses to match.

await fetchExpect(url, 200);
await fetchExpect(url, [200, 201]);

Optional configuration options.

Request init options passed through to fetch.

await fetchExpect(url, 200, {
init: {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer token",
},
body: JSON.stringify({ name: "test" }),
},
});

Status handlers and messages live at the top level alongside other options:

await fetchExpect<ItemResponse>(url, 200, {
init: { headers: { Authorization: "Bearer token" } },
404: "Item not found",
409: (body) => redirect(`/conflict/${body.id}`),
"5xx": "Service unavailable.",
});

errorFactory / extractMessage / fallbackMessage

Section titled “errorFactory / extractMessage / fallbackMessage”

Other createExpectStatus options (except statusField, bodyField, and defaults which are not applicable since fetchExpect always constructs { status, body } internally).

await fetchExpect(url, 200, {
errorFactory: (message) => new CustomError(message),
extractMessage: (body) => (body as { detail?: string }).detail,
fallbackMessage: "Request failed",
});
  1. Calls fetch with the URL and any init options
  2. Parses the response as JSON
  3. Validates the status using expectStatus
  4. Returns the typed body on success
  5. Throws an error on non-success status

If the response is not valid JSON, the body is set to null and the error is thrown with the fallback message.

fetchExpect is exported as a subpath to keep the main bundle small:

import { fetchExpect } from "expect-status/fetch";