Interceptors
Add middleware to the request/response pipeline. Auth, logging, transforms — all async-aware.
Request Interceptors
Run before fetch. Each receives the config and must return it (modified or not).
// Add auth header to every request
api.interceptors.request.use((config) => ({
...config,
headers: {
...config.headers,
Authorization: `Bearer ${getToken()}`,
},
}))Response Interceptors
Run after fetch and parsing. Each receives the response and must return it.
// Unwrap nested API responses
api.interceptors.response.use((response) => ({
...response,
data: response.data.result,
}))Async Interceptors
Interceptors can be async functions — they are awaited automatically in sequence.
// Async interceptors are awaited automatically
api.interceptors.request.use(async (config) => {
const token = await refreshTokenIfNeeded()
return {
...config,
headers: { ...config.headers, Authorization: `Bearer ${token}` },
}
})Error Handlers
use() accepts an optional second argument — an error handler that catches if the interceptor throws.
// Second argument is an error handler
api.interceptors.request.use(
(config) => {
throw new Error("something went wrong")
},
(error) => {
console.error("Interceptor failed:", error)
// Error is caught — request continues without this interceptor's changes
}
)Ejecting
Remove a registered interceptor by its ID.
// Register returns an ID
const id = api.interceptors.request.use(myInterceptor)
// Remove by ID
api.interceptors.request.eject(id)Execution Order
Multiple interceptors run in registration order. Each passes its result to the next.
// Interceptors run in registration order (FIFO)
api.interceptors.request.use(first) // runs 1st
api.interceptors.request.use(second) // runs 2nd
api.interceptors.request.use(third) // runs 3rd
// Each receives the output of the previous oneExample: Logging
// Logging interceptor
api.interceptors.response.use((response) => {
console.log(`[${response.status}] ${response.config.method} ${response.config.url}`)
return response
})