# Cancellation (/docs/cancellation)



Cancellation is **off by default**. Turn it on once:

```ts
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`.

<CancelDemo />

## Three ways to cancel [#three-ways-to-cancel]

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

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

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

```ts
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:

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

## Stale search results [#stale-search-results]

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

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

## What a canceled request returns [#what-a-canceled-request-returns]

It **resolves**, it doesn't throw:

```ts
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 })`.
