Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to return response in interceptors #224

Closed
1 task
trandaison opened this issue Mar 13, 2023 · 11 comments
Closed
1 task

How to return response in interceptors #224

trandaison opened this issue Mar 13, 2023 · 11 comments

Comments

@trandaison
Copy link

Describe the feature

Currently, we already have interceptors, however it's just an synchronous callback on request/response/error...
Is there any way to return a response in response interceptors? This is an example of axios

  axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  }, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
  });

Use case:
I want to catch error 401 in onResponseError then refresh access token, then retry the api call and return the new response from retry request.

Currently, the callback calls but the first request still returns error.

Additional information

  • Would you be willing to help implement this feature?
Copy link
Member

The handlers can be async and I believe you can modify the context.response - have you tried that?.

@trandaison
Copy link
Author

I didn't know that, I will try and response in this thread later, thanks for supporting @danielroe 🙇

@trandaison
Copy link
Author

trandaison commented Mar 14, 2023

I confirm that modifying the context.response is working. It could be useful if the docs mention about this @danielroe
I close this issue 🙇

@ohappykust
Copy link

Hi!
@trandaison , i tried to solve the same problem, but my response didn't change after modifying context.response.
Can you show a small area of the code where you modify old response to new request response?

Thanks!

@trandaison
Copy link
Author

trandaison commented Jun 15, 2023

@happykust sure! something likes this

const api = async (url: string) => {
  const resp = await $fetch(url, {
    onResponseError: async (context) => {
      // Do something
      const res: any = await api(url);
      context.response = res;
    },
  });
  return resp;
};

Please note that this only work when you modify the context.response inside onResponseError, it's not working with onResponse callback.

@KaKi87
Copy link

KaKi87 commented Aug 16, 2023

Hello,
Any news on adding this to the README ?
Thanks

@pi0
Copy link
Member

pi0 commented Aug 16, 2023

PR welcome to improve docs 👍

@trandaison
Copy link
Author

@danielroe It seems some thing's wrong in new version, modify the context.response is not working anymore.
The response data is modified how ever the error status is still there.
The response body is success but the original request falls to catch statement.

@trandaison trandaison reopened this Mar 15, 2024
@hymair
Copy link

hymair commented Mar 23, 2024

I have the exact same scenario. I am refreshing tokens on 401 and I want to catch the error in that handler. Unfortunately even though the refresh is successful and I re-run the original request, the first request throws the error and Nuxt displays an error page.

Here's a code snippet:

export const apiClient = ofetch.create({
    onRequest: ({ options }) => {
        options.headers = authHeaders()
    },
    onResponseError: async ({ request, response, options }) => {
        const authStore = useAuthStore()
        const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY)

        // refresh tokens & re-attempt request
        if (response.status === StatusCodes.UNAUTHORIZED) {
            if (
                response._data.code === 'invalid_access_token' &&
                refreshToken
            ) {
                await authStore.refreshTokens()
                response = await apiClient(request, {
                    ...options,
                    headers: authHeaders(),
                })
            } else {
                return authStore.onLogout()
            }
        }
    },
})

@trandaison
Copy link
Author

trandaison commented Mar 24, 2024

@hymair I was faced the same problem as well and I finally figured it out. There are some points I can tell you:

Firstly, if you want to retry an 401 response and turn the request from error to success then you should use onResponse instead of onResponseError. Cuz onResponse will be executed before onResponseError, where the context.response was overridden, it will stop raising error from the original request.

Secondly, the options object from onResponseError (and onResponse) callback is different from the one from the onRequest. Be careful, because you might lose some options from the original request.

Finally, you don't override the original response by assigning it a new value, javascript does not work like that

response = await apiClient(request, { ... })

This mean you are re-assigning a new value for the response variable, not for the context.response.
At least, it should be something like this:

async onResponse(context) {
  context.response = await apiClient(request, { ... })
}

However, I believe there is some other properties in the context need to be overridden too (such as status). So, I would do this instead

async onResponse(context) {
  context.response = await apiClient(request, {
    onResponse(ctx) {
      Object.assign(context, ctx);
    }
  })
}

Btw, I already write an authentication module for Nuxt 3: https://github.com/trandaison/nuxt-3-auth. It support refresh token too, just give it a try if possible 🙏

You also find the code where I process the refresh access token logic here

@trandaison
Copy link
Author

I close this issue, see this comment #224 (comment) to solve the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants