@liam-public/auth-fetch
v0.1.0
Published
A fetch wrapper that injects a bearer token and transparently refreshes it on 401 — drop-in for tRPC httpBatchLink, axios, or plain fetch.
Readme
@liam-public/auth-fetch
A fetch wrapper that injects a bearer token and transparently refreshes it on 401 — so
your data layer never has to think about token expiry. Built for the common case: tRPC
clients (which accept a fetch), but works with axios or plain fetch too.
Why
tRPC httpBatchLink/httpLink take a fetch. Pass this one and access-token expiry is handled
for you: on a 401 it refreshes once (concurrent 401s share a single refresh) and retries the
request with the new token.
Usage
import { createRefreshingFetch } from '@liam-public/auth-fetch'
import { createTRPCClient, httpBatchLink } from '@trpc/client'
const authFetch = createRefreshingFetch({
getToken: () => authClient.getAccessToken(), // sync or async
refresh: () => authClient.refresh(), // returns the new token (or null)
onAuthError: () => authClient.signInRedirect(), // refresh failed → re-auth
})
const client = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: '/api/trpc', fetch: authFetch })],
})Options
getToken()— current access token (sync or async).refresh()— refresh and return the new token (or null); de-duplicated across concurrent 401s.onAuthError()— called when refresh yields no token.fetch— underlying fetch (default global).isUnauthorized(res)— what counts as "expired" (defaultstatus === 401).headerName/scheme— defaultAuthorization/Bearer.
Request bodies must be re-readable for the retry (tRPC sends string bodies, so this holds).
