[Interceptors] Creating an API client with automatic token refresh functionality
See original GitHub issueHi!
We talked about this yesterday over on the Nuxt discord’s #nuxt3 channel, but since it’s more of an ohmyfetch thing, I decided to create the issue here instead of the nuxt/framework repo.
We’re currently firing up a development effort for a new Nuxt3 project in our company (we’ve previously used Nuxt2 in some projects). Axios was the preferred way of doing API requests previously but since ohmyfetch is now the recommended choice, I thought of rewriting our API client wrapper to use ohmyfetch instead of Axios.
I’m sure the problem is familiar to most if not all of you: how to write an API client that can automatically refresh your access tokens behind the scenes? This is pretty much our previous solution that uses Axios’ interceptors:
const createAPIClient = () => {
const apiClient = axios.create({ headers, baseUrl })
apiClient.interceptors.request.use(config => {
const accessTokens = authStore.tokens // { accessToken: '', refreshToken: '' }
if (tokens) {
config.headers.common.Authorization = `Bearer ${tokens.accessToken}`
}
return config
})
apiClient.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config
// .isRetry is a non-axios property we use to differentiate the actual request from the one we're firing in this interceptor
if (error.response?.status === 401 && !originalRequest.isRetry) {
originalRequest.isRetry = true
try {
// fetch new tokens from our API
const refreshToken = authStore.refreshToken
const { data } = axios.post('/our/nuxt/server-middleware/auth/route/that/proxies/to/our/api/', { refreshToken })
// simplified for brevity
setNewAccessTokensToStore(data)
// retry the original request
originalRequest.headers = { ...originalRequest.headers, Authorization: `Bearer ${data.refreshToken}` }
return apiClient(originalRequest)
} catch (refreshError) {
return Promise.reject(refreshError)
}
}
}
)
return apiClient
}
Historically we’ve done this by creating one larger doRequest
function that gets called recursively, but it was refactored to use interceptors.
I’d like to replicate this functionality using ohmyfetch, so that I could do something like:
const $customFetch = () => {
const client = $fetch.create({
baseURL: 'https://example.com',
async onRequest(ctx => {
// like apiClient.interceptors.request.use
}),
async onResponseError(ctx => {
// like apiClient.interceptors.response.use
})
})
// in any Nuxt3 component
const { data, pending, error, refresh } = await useAsyncData('getSomeData', () => $customFetch('/some/endpoint/in/our/api'))
and the access tokens get set to request headers and possibly refreshed automatically.
I was checking out ohmyfetch’s interceptors, but apparently they don’t return anything, only functioning as hooks where you can set up logging or do something else, instead of acting as sort of middleware like Axios’ counterparts. This leads me to the conclusion that this probably isn’t currently possible with ohmyfetch? It’s no problem to use the recursive approach we had previously, but I feel that this is a bit of a shortcoming with the current interceptors.
Issue Analytics
- State:
- Created a year ago
- Reactions:8
- Comments:8 (1 by maintainers)
Top GitHub Comments
Hi, I’m having a similar flow/issue with Nuxt 3 and was wondering if:
Sorry to bother but I’m starting a project with Nuxt 3 at work. I’m just trying to avoid any issues. Thanks for all the hard work!
Cheers
Thanks for the feedback @evfoxin @attevaltojarvi
As a summary what I could understand is missing:
Actually it is currently possible to achieve this by modification to the context but it is probably more convenience to allow chaining.