TanStack Query doesn't care how you fetch. It only asks one thing of your queryFn: return data, throw on failure, and respect the AbortSignal.

Most HTTP clients don't do that out of the box, so every project grows the same glue.

The usual glue

queryFn: async ({ signal }) => { try { const res = await axios.get('/users', { signal }) return res.data } catch (e) { if (axios.isAxiosError(e) && e.response?.status === 401) { await refreshToken() // what if 5 queries hit 401 at once? // retry...? } throw normalizeError(e) } } 

Multiply that by every endpoint, add SSR cookie forwarding and an SSE stream that needs an Authorization header, and you have a small framework nobody meant to write.

The same thing with tanstack-fetch

import { createFetch } from 'tanstack-fetch' import { createAuthInterceptor, createStatusInterceptor } from 'tanstack-fetch/plugins' export const api = createFetch({ baseUrl: 'https://api.example.com', interceptors: [ createAuthInterceptor({ getToken: () => localStorage.getItem('access_token') }), createStatusInterceptor({ 401: () => logout() }), ], }) useQuery({ queryKey: ['users'], queryFn: ({ signal }) => api.get<User[]>('/users', { signal }), }) 
  • Returns the parsed body directly
  • Throws FetchError, and isFetchError(e) gives you status and body, fully typed
  • Passes signal through, so Query cancellation just works

Typed errors in the UI

const { error } = useQuery(...) if (isFetchError(error) && error.status === 404) return <NotFound /> 

Only pay for what you use

Import What gzip
tanstack-fetch slim HTTP core 3.57KB
tanstack-fetch/plugins interceptors 1.13KB
tanstack-fetch/sse + SSE, upload, progress 6.33KB
tanstack-fetch/react provider + hooks 0.99KB

There are also entry points for Vue/Nuxt, Solid, Svelte, Angular, tRPC and a DevTools dock.

SSE with auth

The browser's EventSource can't send headers. tanstack-fetch/sse can:

import { createFetch } from 'tanstack-fetch/sse' const api = createFetch({ baseUrl: 'https://api.example.com', getToken: () => localStorage.getItem('access_token'), }) const stream = api.sse<OrderEvent>('/orders/stream', { onMessage: (data) => console.log(data), }) stream.close() 

Migrating

npx tanstack-fetch doctor 

It scans your project and scaffolds a migration from axios, ky or ofetch.

Links

Not an official TanStack package. It's built for the same mental model. I'd love feedback in the comments, especially on where the core/plugin line should be.