@mrzr/api-client

Cancellation

Cancel in-flight requests when the user leaves a page, closes a modal or types the next search keystroke. Cancel by key, group, URL pattern or scope.

Cancellation is off by default. Turn it on once:

const api = createClient({ baseUrl, cancel: true });   // GET requests become cancelable

Only GET is covered, because cancelling a read is always safe. A write may already have reached the server, so opt writes in one by one with cancelable: true.

Cancel what the user left behind

Start three 4-second requests, then cancel one of them or the whole page. A canceled request resolves with canceled: true instead of throwing.

  • GET /usersnot sent
  • GET /ordersnot sent
  • GET /reportsnot sent
api.get("/users", { cancelKey: "users", cancelGroup: "page" });
api.cancel("users");  // one request
api.cancel("page");   // everything the page started

Three ways to cancel

By key or group. Name a request, then cancel it by name:

api.get("/search", { cancelKey: "search", params: { q } });
api.cancel("search");

By URL. Cancel everything under a path, or everything in flight:

api.cancel("/api/products");   // /api/products, /api/products/12, …
api.cancel();                  // all tracked requests, e.g. on route change

By scope. Group what a modal or page starts, and cancel it on close:

const scope = api.cancelScope("product-modal");
await scope.get("/products/12");
scope.cancel();   // on close

Stale search results

takeLatest cancels the previous request with the same key, so only the last keystroke's results arrive:

api.get("/search", { cancelKey: "search", takeLatest: true, params: { q } });

What a canceled request returns

It resolves, it doesn't throw:

const res = await api.get("/users", { cancelKey: "users" });
if (res.canceled) return;   // the user moved on; not an error

This is deliberate: if a cancellation threw, TanStack Query would treat it as a failure and retry the request you just canceled. If you'd rather it throw, set cancel: { throwOnCancel: true }.

TanStack Query and SWR can also cancel through their own signal. Pass it along with api.get(url, { signal }).

On this page