tanstack-fetch 1.4.0 introduces tanstack-fetch/devtools: a development-only DevTools dock for inspecting requests directly from the createFetch pipeline.
It tracks:
- HTTP
- SSR / Edge
- SSE
- tRPC
- request attempts
- call sites
- SSE events
- request timing
And it can jump from a request directly to the source file that triggered it.
The problem
The browser Network tab can tell you:
GET /api/profile
200
143ms
But when debugging an application, you often need:
Who called this?
Was this SSR?
Was the request retried?
Did SSE actually close?
Where did this request originate?
Did tRPC pass through my interceptor?
That's what the new DevTools integration is designed to answer.
Install
npm i tanstack-fetch@1.4.0
import { createFetch } from 'tanstack-fetch'
import { setupDevtools } from 'tanstack-fetch/devtools'
const api = createFetch({
baseUrl: import.meta.env.VITE_API_URL,
plugins: ['trace'],
})
if (import.meta.env.DEV) {
setupDevtools(api)
}
trace is useful because it gives each request a stable ID:
requestId:attempt
That makes retries and repeated requests much easier to distinguish.
Configuration
setupDevtools(api, {
http: true,
sse: true,
ssr: true,
trpc: true,
open: false,
})
Shortcut:
Alt + Shift + F
macOS:
⌥ ⇧ F
What does it record?
HTTP
method
path
status
duration
attempts
call site
SSR
The DevTools can distinguish SSR / Edge requests and show incoming authentication context without dumping secrets.
SSE
SSE requests have a stream lifecycle:
pending → live → success
or:
pending → live → error
or:
pending → live → aborted
Each SSE event can also be inspected:
id
name
payload
tRPC
The tRPC adapter marks operations using:
meta.operation = 'trpc'
This lets DevTools group and identify them alongside normal HTTP requests.
Call-site tracking
One of the main reasons I built this is source tracing.
Instead of stopping at:
GET /api/profile
you can inspect the application call graph:
ProfilePage
↓
useProfile
↓
profile.hook.ts
↓
api.get()
Then click the relevant frame and open it in:
- Cursor
- Zed
- VS Code
The editor deep link includes the source location.
Why not just use the Network tab?
Because the Network tab observes the browser's network layer.
This DevTools integration observes the fetch abstraction your application actually uses.
| Type | Detection |
|---|---|
| HTTP | normal fetch calls |
| SSR | `source: 'ssr' \ |
| SSE | {% raw %}onSseOpen, events, onSseClose
|
| tRPC | meta.operation = 'trpc' |
This makes it possible to inspect different application-level request types in one place.
Two lifecycle improvements in 1.4.0
Successful tRPC responses
Previously, successful tRPC responses could skip onResponse.
The adapter now invokes onResponse for successful responses while keeping the response body unread for tRPC parsing.
That makes timing and DevTools instrumentation possible.
SSE close lifecycle
SSE previously had no onSseClose lifecycle hook.
Now:
onSseClose
allows the client and DevTools to distinguish an active stream from a cleanly closed one.
Multiple clients, one DevTools store
If HTTP and SSE use separate factories:
import { createFetch } from 'tanstack-fetch/sse'
import {
createDevtools,
mountDevtools,
} from 'tanstack-fetch/devtools'
const dt = createDevtools({
http: true,
sse: true,
ssr: true,
trpc: true,
})
const api = createFetch({
plugins: ['trace', 'sse-resume'],
interceptors: [dt.interceptor],
})
mountDevtools({
store: dt.store,
open: true,
})
Both clients can report into the same store and the same dock.
Live demo
You can try it directly in the browser:
No installation required.
Trigger HTTP, upload, SSR, SSE, and tRPC examples, then open the dock with:
Alt + Shift + F
What is tanstack-fetch?
tanstack-fetch is a typed Fetch client designed around the TanStack Query model.
The core contract is intentionally small:
success → data
HTTP error → FetchError
cancellation → AbortSignal
Optional modules add:
- SSR
- SSE
- uploads
- React
- tRPC
- DevTools
It is not an official TanStack package.
Development only
Keep DevTools behind your development environment:
if (import.meta.env.DEV) {
setupDevtools(api)
}
Links
If you try it in a real application, let me know what you would add next: filters, export, React integration, or something else.
Top comments (0)