Tutorial

How to Fetch with Axios Interceptors

Use Axios interceptors to attach auth tokens and retry on 401, so every request carries the token and a expired token triggers a refresh transparently.

On this page

  1. Introduction
  2. Add a request interceptor
  3. Attach the token
  4. Add a response interceptor
  5. Refresh on 401
  6. Retry the original
  7. Test interceptors
  8. Common pitfalls
  9. Conclusion

Introduction

The hardest part of attach auth and retry is not writing the code — it is deciding what the code is allowed to reject. A Axios interceptors setup that accepts everything is easy to ship and expensive to operate, because every malformed input becomes a production surprise. The discipline is to enumerate the shapes the system actually receives, encode them in Axios interceptors, and let the library reject the rest loudly. This keeps the happy path fast and the failure path visible.

Choosing Axios interceptors for attach auth and retry is a trade, not a winner. You give up some flexibility to get a hard boundary, and the exchange pays off when the boundary catches a class of bugs that would otherwise surface at 3 a.m. Document the trade at the call site so the next reader does not undo the guard thinking it is a bug. The reader who does not know why the guard exists is the reader who removes it.

The sections below walk through the concrete decisions: where to put the axios interceptors check, how to keep it fast under load, what to do when the contract changes, and how to test the failure paths so they degrade on purpose instead of by accident.

Add a request interceptor

Add a request interceptor is the first decision because it determines what the rest of the Axios interceptors pipeline can assume. A wrong choice here turns into attach auth and retry under load, and the invariant you thought Axios interceptors guaranteed no longer holds. Measure the impact before committing, because the cost of a bad choice is paid in incidents, not in a code review.

The test for Add a request interceptor is the one most teams skip: drive axios interceptors to the failing condition and assert the system degrades gracefully. Write it with a fake clock for time-based logic, a stub for the failure path, and an assertion on the user-visible outcome. A test that passes by timing out is a hope, not a test.

api.interceptors.request.use(c => { c.headers.Authorization = `Bearer ${token}`; return c; });
api.interceptors.response.use(r => r, async e => {
  if (e.response?.status === 401) { await refreshToken(); return api(e.config); }
  throw e;
});

The javascript above is the shape to ship for attach auth and retry. It is small enough to audit and specific enough that the failure behavior is obvious. Adapt the constants to your traffic and latency budget, and resist the urge to handle cases the system has not seen yet.

Attach the token

The mechanism behind Attach the token is straightforward in Axios interceptors, but the consequences ripple. A wrong choice here turns into attach auth and retry under load, and the invariant you thought Axios interceptors guaranteed no longer holds. Measure the impact before committing, because the cost of a bad choice is paid in incidents, not in a code review.

The test for Attach the token is the one most teams skip: drive axios interceptors to the failing condition and assert the system degrades gracefully. Write it with a fake clock for time-based logic, a stub for the failure path, and an assertion on the user-visible outcome. A test that passes by timing out is a hope, not a test.

Add a response interceptor

Add a response interceptor is where most Axios interceptors implementations leak performance. A wrong choice here turns into attach auth and retry under load, and the invariant you thought Axios interceptors guaranteed no longer holds. Measure the impact before committing, because the cost of a bad choice is paid in incidents, not in a code review.

The test for Add a response interceptor is the one most teams skip: drive axios interceptors to the failing condition and assert the system degrades gracefully. Write it with a fake clock for time-based logic, a stub for the failure path, and an assertion on the user-visible outcome. A test that passes by timing out is a hope, not a test.

test('retries after refresh', async () => {
  api.mockRejectedValueOnce({ response: { status: 401 } }).mockResolvedValueOnce('ok');
  expect(await api.get('/x')).toBe('ok');
});

Pin the behavior with this test. A Axios interceptors setup that passes this test degrades on purpose instead of by accident, which is the difference between a managed failure and an incident.

Refresh on 401

The hard part of Refresh on 401 is not the happy path — it is what happens when axios interceptors fails halfway. A wrong choice here turns into attach auth and retry under load, and the invariant you thought Axios interceptors guaranteed no longer holds. Measure the impact before committing, because the cost of a bad choice is paid in incidents, not in a code review.

The test for Refresh on 401 is the one most teams skip: drive axios interceptors to the failing condition and assert the system degrades gracefully. Write it with a fake clock for time-based logic, a stub for the failure path, and an assertion on the user-visible outcome. A test that passes by timing out is a hope, not a test.

Retry the original

Retry the original should be observable without a debugger. A wrong choice here turns into attach auth and retry under load, and the invariant you thought Axios interceptors guaranteed no longer holds. Measure the impact before committing, because the cost of a bad choice is paid in incidents, not in a code review.

The test for Retry the original is the one most teams skip: drive axios interceptors to the failing condition and assert the system degrades gracefully. Write it with a fake clock for time-based logic, a stub for the failure path, and an assertion on the user-visible outcome. A test that passes by timing out is a hope, not a test.

Test interceptors

Test interceptors is a trade, not a winner. A wrong choice here turns into attach auth and retry under load, and the invariant you thought Axios interceptors guaranteed no longer holds. Measure the impact before committing, because the cost of a bad choice is paid in incidents, not in a code review.

The test for Test interceptors is the one most teams skip: drive axios interceptors to the failing condition and assert the system degrades gracefully. Write it with a fake clock for time-based logic, a stub for the failure path, and an assertion on the user-visible outcome. A test that passes by timing out is a hope, not a test.

Common pitfalls

The common pitfalls in attach auth and retry all reduce to one mistake: treating Axios interceptors as a formality instead of a contract. The contract is the thing the rest of the system relies on, and a weak contract propagates weakness everywhere.

  • Skipping the axios interceptors boundary so attach auth and retry enters unvalidated.
  • Assuming Axios interceptors is fast without measuring the tail latency under load.
  • Letting attach auth and retry fail silently instead of failing loudly with a metric.
  • Testing only the happy path so the axios interceptors failure surfaces in production.
  • Sharing mutable state across requests so one bad input poisons the next.

Conclusion

Do not conflate the conclusion with the implementation. They look similar from the outside but the failure modes differ: Axios interceptors fails fast and the caller knows, while an unchecked path fails slow and the caller guesses. Choose the one whose failure mode your system can absorb, and document the choice at the call site.

Self-check: write a test that drives axios interceptors to the failing condition and assert the system degrades gracefully. Then attach auth and retry is a property you maintain, not a feature you hope for.

Sources: Axios interceptors, Token refresh

FAQ

Avoid retry loop?
Set a _retry flag on the config. Skip if already set.
Multiple 401 at once?
Queue them and retry after one refresh. Use a promise singleton.

Related Examples