If you've used Nuxt you've probably encountered the handy useFetch composable:
<script setup lang="ts"> const { data, status, error, refresh, clear } = await useFetch('/api/modules') </script>
This simplifies fetching data, but what if you have a multitude of APIs that all require authentication? Adding headers to each call gets tedious fast.
Enter interceptors.
To add global interceptors, we'll build a custom composable wrapper around $fetch. This is especially valuable when your API calls consistently need authorization headers.
As a foundation, let's use the same project from my previous blog post on Authentication in Nuxt 3.
let's start by creating a new composable under composable folder composables/useAuthFetch.ts
import type { UseFetchOptions } from 'nuxt/app'; const useAuthFetch = (url: string | (() => string), options: UseFetchOptions<null> = {}) => { const customFetch = $fetch.create({ baseURL: 'https://dummyjson.com', onRequest({ options }) { const token = useCookie('token'); if (token?.value) { console.log('[fetch request] Authorization header created'); options.headers = options.headers || {}; options.headers.Authorization = `Bearer ${token.value}`; } }, onResponse({ response }) { console.info('onResponse ', { endpoint: response.url, status: response?.status, }); }, onResponseError({ response }) { const statusMessage = response?.status === 401 ? 'Unauthorized' : 'Response failed'; console.error('onResponseError ', { endpoint: response.url, status: response?.status, statusMessage, }); throw showError({ statusCode: response?.status, statusMessage, fatal: true, }); }, }); return useFetch(url, { ...options, $fetch: customFetch, }); }; export default useAuthFetch;
Explanation:
you can find out more about the interceptors here
Now, whenever you need to fetch data from an authenticated API, simply use useAuthFetch instead of useFetch, and the authorization will be handled seamlessly.
<template> <div v-if="user">Welcome back {{ user.email }}</div> <div v-else>loading...</div> </template> <script lang="ts" setup> const { data: user } = await useAuthFetch('/auth/me'); </script>
When you inspect the network call you can see the baseUrl is correct and the Authorization header is present
In my interceptors, I have added some logs this can be useful if you have tools like Sentry in your application.
How to add Sentry to Nuxt: https://www.lichter.io/articles/nuxt3-sentry-recipe/
in the onRequest interceptor you could add a breadcrumb to sentry
import * as Sentry from '@sentry/vue'; Sentry.addBreadcrumb({ type: 'http', category: 'xhr', message: ``, data: { url: `${options.baseURL}${request}`, }, level: 'info', });
if your backend returns a tracingId you could also add a tag and context with sentry to link errors with an endpoint
onResponseError you could add context breadcrumb and tag
import * as Sentry from '@sentry/vue'; Sentry.setContext('http-error', { endpoint: response?.url, tracingId: 123, status: response?.status, }); Sentry.addBreadcrumb({ type: 'http', category: 'xhr', message: ``, data: { url: response?.url, status_code: response?.status, }, level: 'error', }); Sentry.setTag('tracingId', '123');
replace tracingIdwith whatever custom tracing log your backend returns
The above is the detailed content of Custom fetch with Interceptors and logs in nuxt 3. For more information, please follow other related articles on the PHP Chinese website!