axios-auth-refresh
Automatically refresh expired auth tokens and retry failed requests with Axios interceptors.
Repository Health
Technical Analysis
axios-auth-refresh is a small TypeScript library that wires an Axios response interceptor to automatically handle expired authorization. When a request fails with a 401 (or any status you configure), it calls your refresh-token logic, queues any other requests that fail in the meantime, and replays them once a new token is available — all without the caller having to write retry plumbing by hand.
It ships as a single factory function, createAuthRefresh, with sensible defaults and a handful of options (custom status codes, a shouldRefresh predicate, request deduplication, a separate retry instance, an onRetry hook, network-error interception, and a max-retry guard against infinite loops), making it a drop-in addition to any codebase already using Axios.
What You Get
- A single
createAuthRefresh(instance, refreshAuthCall, options)function that installs a response interceptor and returns its id for manual ejection - Automatic deduplication of concurrent refresh calls — multiple simultaneous 401s trigger exactly one refresh, with other failed requests queued and resolved together
- A
skipAuthRefreshper-request config flag to opt individual calls (like the refresh endpoint itself) out of interception - Configurable interception via
statusCodes, a customshouldRefresh(error)predicate, orinterceptNetworkErrorfor CORS-hidden 401s - A
maxRetriesguard that rejects instead of looping forever if the refreshed token still fails - Full TypeScript typings for options, cache, and request-config extensions, published alongside CJS and ESM builds
Common Use Cases
- SPA or mobile-web clients using short-lived JWT access tokens that need silent, transparent refresh on expiry
- Apps with multiple in-flight API calls that must all wait for one refresh cycle instead of firing redundant refresh requests
- Codebases that want refresh logic centralized on the Axios instance instead of duplicated in every request’s catch block
- APIs that signal auth failure via non-standard status codes or network-level errors that need a custom
shouldRefreshrule
Under The Hood
Architecture The library exports a single factory function createAuthRefresh(instance, refreshAuthCall, options) (src/index.ts) that registers an Axios response interceptor. On error, shouldInterceptError (src/utils.ts) decides whether to intercept based on status codes, a custom shouldRefresh predicate, or network-error detection, while consulting a per-instance cache object (skipInstances, refreshCall, requestQueueInterceptorId — src/model.ts) to avoid re-triggering the refresh flow for concurrent failures. When a request should be refreshed, createRefreshCall lazily invokes the caller-supplied refreshAuthCall and memoizes the returned promise on cache.refreshCall so simultaneous 401s share one refresh instead of firing N parallel refreshes; createRequestQueueInterceptor installs a temporary request interceptor that stalls any new outgoing requests on that same promise (running an optional onRetry config mutator) until it resolves, then resendFailedRequest flags the original failed request with skipAuthRefresh and replays it through the (possibly separate) retryInstance. unsetCache in a .finally() tears down the queue interceptor and cache state regardless of outcome, and a maxRetries counter stashed on error.config.__authRefreshRetryCount guards against infinite refresh loops when the retried request keeps failing.
Tech Stack Pure TypeScript with axios declared as a peer dependency (>= 1.0.0) and no runtime dependencies beyond it — the whole implementation is roughly 250 lines across src/index.ts, src/utils.ts, and src/model.ts. It’s built with Rollup (rollup.config.mjs, @rollup/plugin-typescript, @rollup/plugin-terser) into CJS/ESM/.d.ts outputs declared via package.json exports, tested with Jest and ts-jest, linted/formatted with Prettier through Husky pre-commit hooks (pretty-quick --staged), and written in TypeScript 5.8 with strict typing throughout.
Code Quality src/__tests__/index.spec.ts runs roughly 490 lines covering deduplication, custom status codes, shouldRefresh, network-error interception, onRetry, maxRetries, and interceptor cleanup, and the examples/ directory doubles as runnable end-to-end scenarios (basic-refresh, skip-auth-refresh, pause-instance, custom-status-codes, on-retry-callback, network-error). Functions are small and single-purpose with JSDoc comments on every exported utility, error paths wrap user-supplied callbacks (safeShouldRefresh, the onRetry try/catch) so a throwing consumer callback can’t crash the interceptor chain, and public types (AxiosAuthRefreshOptions, AxiosAuthRefreshCache, AxiosAuthRefreshRequestConfig) are centralized in model.ts.
API Design The public surface is a single function, createAuthRefresh(axios, refreshAuthLogic, options), that mirrors Axios’s own idioms (an interceptor-id return value, request-config-shaped options) so there’s near-zero conceptual overhead for anyone already using Axios. A skipAuthRefresh request-config flag and a deprecated-but-still-exported createAuthRefreshInterceptor alias preserve backward compatibility across major versions, options (statusCodes, shouldRefresh, deduplicateRefresh, retryInstance, onRetry, interceptNetworkError, maxRetries) are all optional with sane defaults, and the README documents each one with runnable snippets, keeping time-to-first-working-example low.