Global request and response interceptors #56
-
Hi, I need to add global request and response interceptors that can handle authentication and token refreshing seamlessly. Request Interceptor:
Response Interceptor:
Is there any way to add global interceptors with this module? Any help would be greatly appreciated. Best |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
Sure! Since both generated composables
See below for a full example. The real API is wrapped to handle auth state: import { useAuthStore } from '~/stores/auth'
import { toValue } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import type { MaybeRefOrGetter } from '@vueuse/core'
import type { ApiFetchOptions } from 'nuxt-api-party/dist/runtime/composables/$api'
import type { UseApiDataOptions } from 'nuxt-api-party/dist/runtime/composables/useApiData'
export async function $api<T = any>(
path: string,
opts: ApiFetchOptions & {
auth?: boolean
} = {}
) {
const { auth = true, ..._opts } = opts
const headers: HeadersInit = {
...headersToObject(_opts.headers),
}
if (auth) {
const authStore = useAuthStore()
if (authStore.isLoggedIn) {
if (authStore.isTokenExpired()) await authStore.refresh()
headers.Authorization = `Bearer ${authStore.accessToken}`
}
}
return await $realApi<T>(path, {
..._opts,
headers,
})
}
export async function useApiData<T = any>(
path: MaybeRefOrGetter<string>,
opts: UseApiDataOptions<T> & {
auth?: boolean
} = {}
) {
const { auth, ..._opts } = opts
const authStore = useAuthStore()
const { isLoggedIn } = storeToRefs(authStore)
if (
import.meta.client &&
auth &&
isLoggedIn.value &&
authStore.isTokenExpired()
) {
await authStore.refresh()
}
const asyncData = await useRealApiData<T>(path, {
..._opts,
headers: computed(() => ({
...headersToObject(toValue(_opts.headers)),
...(auth &&
isLoggedIn.value && {
Authorization: `Bearer ${authStore.accessToken}`,
}),
})),
// Instead of watching all fetch options implicitly,
// we watch only the ones we need
watch: false,
})
// Always watch the query
const fetchOptions = reactive({
query: _opts.query,
})
watch(fetchOptions, () => asyncData.refresh())
if (auth) {
watch(isLoggedIn, () => asyncData.refresh())
}
return asyncData
}
function headersToObject(
headers: HeadersInit = {}
): Record<string, string> {
if (headers instanceof Headers || Array.isArray(headers)) {
return Object.fromEntries(headers)
}
return headers
} Remember to try to refresh the token once the user visits the SSR-rendered site: // `plugins/auth.server.ts`
import { useAuthStore } from '~/stores/auth'
export default defineNuxtPlugin(async () => {
const authStore = useAuthStore()
// Refresh the token once on the server
if (authStore.isLoggedIn && authStore.isTokenExpired()) {
await authStore.refresh()
}
}) |
Beta Was this translation helpful? Give feedback.
-
@johannschopplich |
Beta Was this translation helpful? Give feedback.
-
I have one more related question, and I believe it might be interesting for others following this discussion as well. It appears that autocompletion is no longer functioning when I use my new composable with OpenApi. I'm wondering if there's a way to integrate OpenApi types for autocompletion in this scenario? Any insights would be greatly appreciated :) |
Beta Was this translation helpful? Give feedback.
Sure! Since both generated composables
useMyApiData
and$myApi
use ofetch under the hood just likeuseAsyncData
, respectively$fetch
, you can use the supported interceptors.onRequest
onResponse
See below for a full example. The real API is wrapped to handle auth state: