how to send original request from onResponseError?

1. response fails with a 401 2. within onResponseError, perform refresh and get new bearertoken 3. from here, how do i retry my request with the new headers? ive tried..
return await $fetch(ctx.request, ctx.options);
return await $fetch(ctx.request, ctx.options);
but i get a typescript error because it only accepts promise void...
5 Replies
kapa.ai
kapa.ai2w ago
Hi @BinaryArtifex. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To retry a request with new headers after handling a 401 error in Nuxt's onResponseError interceptor, you need to implement a custom fetch solution. Based on the knowledge sources, here's how you can approach this: Creating a Custom Fetch with Retry Logic The best approach is to create a custom fetch composable that handles token refresh and request retries:
export function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
const config = useRuntimeConfig()
const { isSignedIn, getToken, setToken } = useAuthStore()

const defaults: UseFetchOptions<T> = {
baseURL: config.public.apiBaseUrl,
key: url,
server: false,
retry: 1,
retryStatusCodes: [401],
retryDelay: 500,

onRequest({ options }) {
options.headers = isSignedIn
? { Authorization: `Bearer ${getToken()}` }
: {}
},

async onResponseError({ response, options }) {
if (response.status === 401) {
await useFetch('/auth/refresh', {
baseURL: config.public.apiBaseUrl,
method: 'POST',
server: false,
credentials: 'include',

onResponse({ response }) {
setToken(response._data.token) // store token
},
})
}
}
}

const params = defu(options, defaults)
return useFetch(url, params)
}
export function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
const config = useRuntimeConfig()
const { isSignedIn, getToken, setToken } = useAuthStore()

const defaults: UseFetchOptions<T> = {
baseURL: config.public.apiBaseUrl,
key: url,
server: false,
retry: 1,
retryStatusCodes: [401],
retryDelay: 500,

onRequest({ options }) {
options.headers = isSignedIn
? { Authorization: `Bearer ${getToken()}` }
: {}
},

async onResponseError({ response, options }) {
if (response.status === 401) {
await useFetch('/auth/refresh', {
baseURL: config.public.apiBaseUrl,
method: 'POST',
server: false,
credentials: 'include',

onResponse({ response }) {
setToken(response._data.token) // store token
},
})
}
}
}

const params = defu(options, defaults)
return useFetch(url, params)
}
This approach uses the built-in retry functionality of useFetch with retry: 1 and retryStatusCodes: [401] to automatically retry the request after a 401 error, with the updated token from your refresh call. The defu utility merges your custom options with the defaults. Stack Overflow Post Alternative Approach with a Custom Plugin You can also create a custom $fetch instance with a Nuxt plugin:
export default defineNuxtPlugin((nuxtApp) => {
const { session } = useUserSession()

const api = $fetch.create({
baseURL: '<https://api.example.com',>
onRequest({ request, options }) {
if (session.value?.token) {
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
}
},
async onResponseError({ request, response, options }) {
if (response.status === 401) {
// Refresh token logic here
const newToken = await refreshToken()

// Update session
session.value.token = newToken

// Retry the original request with new token
// Note: This would need to be handled differently
}
}
})

return {
provide: {
api
}
}
})
export default defineNuxtPlugin((nuxtApp) => {
const { session } = useUserSession()

const api = $fetch.create({
baseURL: '<https://api.example.com',>
onRequest({ request, options }) {
if (session.value?.token) {
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
}
},
async onResponseError({ request, response, options }) {
if (response.status === 401) {
// Refresh token logic here
const newToken = await refreshToken()

// Update session
session.value.token = newToken

// Retry the original request with new token
// Note: This would need to be handled differently
}
}
})

return {
provide: {
api
}
}
})
Custom useFetch in Nuxt The issue you're facing with TypeScript errors is because onResponseError in Nuxt's useFetch is designed to handle errors, not to retry requests directly. The built-in retry functionality or a custom fetch implementation as shown above is the recommended approach. Remember that when using a custom useFetch wrapper, you should not await it in the composable itself, as that can cause unexpected behavior. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@382140070183960576> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
BinaryArtifex
BinaryArtifexOP2w ago
@kapa.ai how would the custom $fetch be handled differently? provide an example
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'll provide an example of how to handle retrying requests with a custom $fetch instance when dealing with 401 errors and token refreshing. Here's a more complete example of a custom $fetch implementation that properly handles retrying the original request after token refresh:
export default defineNuxtPlugin((nuxtApp) => {
const { session } = useUserSession()

// Create a custom fetch instance
const api = $fetch.create({
baseURL: '<https://api.nuxt.com',>

onRequest({ request, options }) {
// Add authorization header if token exists
if (session.value?.token) {
options.headers = new Headers(options.headers)
options.headers.set('Authorization', `Bearer ${session.value.token}`)
}
},

async onResponseError({ request, response, options }) {
// Handle 401 Unauthorized errors
if (response.status === 401 && !options.retry) {
try {
// Refresh the token
const newToken = await $fetch('/auth/refresh', {
method: 'POST',
credentials: 'include'
})

// Update the session with new token
session.value.token = newToken.token

// Clone the original request options
const newOptions = { ...options, retry: true }

// Set the new token in the headers
newOptions.headers = new Headers(newOptions.headers)
newOptions.headers.set('Authorization', `Bearer ${newToken.token}`)

// Retry the original request with new token
return $fetch(request, newOptions)
} catch (refreshError) {
// If token refresh fails, redirect to login
await nuxtApp.runWithContext(() => navigateTo('/login'))
return Promise.reject(refreshError)
}
}
}
})
return {
provide: {
api
}
}
})
export default defineNuxtPlugin((nuxtApp) => {
const { session } = useUserSession()

// Create a custom fetch instance
const api = $fetch.create({
baseURL: '<https://api.nuxt.com',>

onRequest({ request, options }) {
// Add authorization header if token exists
if (session.value?.token) {
options.headers = new Headers(options.headers)
options.headers.set('Authorization', `Bearer ${session.value.token}`)
}
},

async onResponseError({ request, response, options }) {
// Handle 401 Unauthorized errors
if (response.status === 401 && !options.retry) {
try {
// Refresh the token
const newToken = await $fetch('/auth/refresh', {
method: 'POST',
credentials: 'include'
})

// Update the session with new token
session.value.token = newToken.token

// Clone the original request options
const newOptions = { ...options, retry: true }

// Set the new token in the headers
newOptions.headers = new Headers(newOptions.headers)
newOptions.headers.set('Authorization', `Bearer ${newToken.token}`)

// Retry the original request with new token
return $fetch(request, newOptions)
} catch (refreshError) {
// If token refresh fails, redirect to login
await nuxtApp.runWithContext(() => navigateTo('/login'))
return Promise.reject(refreshError)
}
}
}
})
return {
provide: {
api
}
}
})
This implementation: 1. Creates a custom $fetch instance with your API base URL 2. Adds the current token to requests via onRequest 3. Handles 401 errors in onResponseError by: - Adding a retry flag to prevent infinite loops - Refreshing the token - Creating new options with the updated token - Retrying the original request with the new token - Redirecting to login if refresh fails You can then use this custom fetcher in your components:
<script setup>
const { $api } = useNuxtApp()

// Use directly
const { data } = await useAsyncData('users', () =>
$api('/users')
)

// Or create a custom composable
function useAPI(url, options = {}) {
return useAsyncData(url, () =>
useNuxtApp().$api(url, options)
)
}

// Then use it like this
const { data: products } = await useAPI('/products')
</script>
<script setup>
const { $api } = useNuxtApp()

// Use directly
const { data } = await useAsyncData('users', () =>
$api('/users')
)

// Or create a custom composable
function useAPI(url, options = {}) {
return useAsyncData(url, () =>
useNuxtApp().$api(url, options)
)
}

// Then use it like this
const { data: products } = await useAPI('/products')
</script>
This approach gives you full control over the retry logic while maintaining the benefits of Nuxt's data fetching system. Custom useFetch in Nuxt ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@382140070183960576> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?