With TanStack Query
expect-status pairs naturally with TanStack Query — it returns a promise that resolves to the typed body or throws, which is exactly what queryFn and mutationFn expect.
Queries
Section titled “Queries”import { useQuery } from "@tanstack/react-query";import { expectStatus } from "expect-status";
type OrgResponse = | { status: 200; body: { id: string; name: string } } | { status: 404; body: { message: string } };
function useOrganisation(id: string) { return useQuery({ queryKey: ["org", id], queryFn: () => expectStatus(200, client.getOrganisation({ params: { id } })), });}
// data is typed as { id: string; name: string }// error is the thrown ExpectStatusErrorconst { data, error, isLoading } = useOrganisation("org_1");No wrappers or adapters needed — expectStatus returns a promise that resolves on success and throws on error, which maps directly to TanStack Query’s data / error states.
Mutations
Section titled “Mutations”import { useMutation } from "@tanstack/react-query";import { expectStatus } from "expect-status";
function useCreateOrganisation() { return useMutation({ mutationFn: (data: CreateOrgInput) => expectStatus(201, client.createOrganisation({ body: data }), { 409: (body) => redirect(`/org/${body.organisationId}`), 422: "Invalid organisation details.", }), onSuccess: (org) => { // org is typed as the 201 body queryClient.invalidateQueries({ queryKey: ["orgs"] }); }, onError: (err) => { toast.error(err.message); }, });}With throws: false
Section titled “With throws: false”For mutations where you want structured results instead of exceptions:
function useCreateOrganisation() { return useMutation({ mutationFn: async (data: CreateOrgInput) => { const result = await expectStatus( 201, client.createOrganisation({ body: data }), { throws: false }, ); if (!result.ok) { return { error: result.error.message }; } return { data: result.data }; }, });}With a custom instance
Section titled “With a custom instance”import { createExpectStatus } from "expect-status";
const expectStatus = createExpectStatus({ fallbackMessage: "Something went wrong.", defaults: { 401: "Please sign in.", 403: "You do not have permission.", "5xx": "Service unavailable. Please try again.", }, onError: (err, response) => { Sentry.captureException(err, { extra: { status: response.status }, }); },});
function useItems() { return useQuery({ queryKey: ["items"], queryFn: () => expectStatus(200, client.listItems()), });}Default messages and observability hooks apply automatically to every query — no per-hook configuration needed.
Optimistic updates
Section titled “Optimistic updates”function useUpdateItem() { return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateItemInput }) => expectStatus(200, client.updateItem({ params: { id }, body: data }), { 409: "Item was modified by someone else. Please refresh.", }), onMutate: async ({ id, data }) => { await queryClient.cancelQueries({ queryKey: ["item", id] }); const previous = queryClient.getQueryData(["item", id]); queryClient.setQueryData(["item", id], (old) => ({ ...old, ...data })); return { previous }; }, onError: (err, { id }, context) => { queryClient.setQueryData(["item", id], context?.previous); toast.error(err.message); }, onSettled: (_, __, { id }) => { queryClient.invalidateQueries({ queryKey: ["item", id] }); }, });}Suspense queries
Section titled “Suspense queries”import { useSuspenseQuery } from "@tanstack/react-query";
function useOrganisation(id: string) { return useSuspenseQuery({ queryKey: ["org", id], queryFn: () => expectStatus(200, client.getOrganisation({ params: { id } }), { 404: "Organisation not found.", }), });}
// In a component wrapped with <Suspense> and <ErrorBoundary>:// - Loading state handled by Suspense// - Error state handled by ErrorBoundary// - data is typed as the 200 body (no undefined)With recover
Section titled “With recover”Use recover for queries that should gracefully degrade:
function useOrganisation(id: string) { return useQuery({ queryKey: ["org", id], queryFn: () => expectStatus(200, client.getOrganisation({ params: { id } }), { recover: () => null, // return null instead of throwing }), });}
// data is typed as Organisation | nullSee also
Section titled “See also”- TanStack Query documentation
- Quick Start — getting started with expect-status
- Error Resolution — how errors are resolved