Authenticated Fetch
auth.fetch()
Section titled “auth.fetch()”auth.fetch() is a drop-in replacement for the native fetch(). It adds authentication automatically and retries on 401.
const res = await auth.fetch('/api/profile');
const data = await res.json(); const res = await auth.fetch('/api/profile');
const data = await res.json(); It has the same signature as fetch() — same RequestInit options, same Response return. Headers, streaming, AbortController — everything works.
How It Works
Section titled “How It Works”- Sends the request with credentials (cookie mode) or
Authorizationheader (bearer mode) - If the response is 401, refreshes the access token
- Retries the original request once with the new token
- If the retry also fails, returns the 401 response
Multiple concurrent 401s share a single refresh call — no thundering herd.
Examples
Section titled “Examples”// JSON POST
const res = await auth.fetch('/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'test' }),
});
// Streaming
const stream = await auth.fetch('/api/stream');
const reader = stream.body.getReader();
// AbortController
const controller = new AbortController();
await auth.fetch('/api/slow', { signal: controller.signal }); // JSON POST
const res = await auth.fetch('/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'test' }),
});
// Streaming
const stream = await auth.fetch('/api/stream');
const reader = stream.body.getReader();
// AbortController
const controller = new AbortController();
await auth.fetch('/api/slow', { signal: controller.signal }); getToken()
Section titled “getToken()”For use with custom HTTP clients (Axios, TanStack Query, etc.) in bearer mode:
const token = await auth.getToken();
// Returns the current access token, or null in cookie mode const token = await auth.getToken();
// Returns the current access token, or null in cookie mode getToken() refreshes automatically if the token is expired. Concurrent calls share a single refresh.
// Cookie mode — just enable credentials
axios.defaults.withCredentials = true; // Cookie mode — just enable credentials
axios.defaults.withCredentials = true; // Bearer mode — attach token via interceptor
axios.interceptors.request.use(async (config) => {
const token = await auth.getToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
}); // Bearer mode — attach token via interceptor
axios.interceptors.request.use(async (config) => {
const token = await auth.getToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
}); TanStack Query
Section titled “TanStack Query”const { data } = useQuery({
queryKey: ['profile'],
queryFn: async () => {
const token = await auth.getToken();
const res = await fetch('/api/profile', {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
},
}); const { data } = useQuery({
queryKey: ['profile'],
queryFn: async () => {
const token = await auth.getToken();
const res = await fetch('/api/profile', {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
},
}); Cookie Mode vs Bearer Mode
Section titled “Cookie Mode vs Bearer Mode”| Cookie Mode | Bearer Mode | |
|---|---|---|
auth.fetch() adds | credentials: 'include' | Authorization: Bearer <token> |
getToken() returns | null (tokens are in cookies) | The access token string |
| Who stores tokens | Browser (HttpOnly cookies) | You (via TokenStorage) |