Пользовательский useFetch в Nuxt

Как создать пользовательскую функцию для вызова внешнего API в Nuxt.

Во фронтенде на Nuxt часто вызывают внешнее API; удобно задать для этих запросов общие параметры по умолчанию (базовый URL, заголовки, обработка ошибок).

Утилита $fetch (используемая композаблом useFetch) намеренно не является глобально настраиваемой. Это важно для того, чтобы поведение получения данных в вашем приложении оставалось последовательным, и другие интеграции (например, модули) могли полагаться на поведение основных утилит, таких как $fetch.

Nuxt позволяет завести отдельный экземпляр «фетча» под ваш API — или несколько, если бэкендов несколько.

Пользовательский $fetch

Создайте экземпляр $fetch в плагине Nuxt.

$fetch — это настроенный экземпляр ofetch, который поддерживает добавление базового URL вашего сервера Nuxt, а также прямые вызовы функций во время SSR без лишнего HTTP-запроса туда-обратно.

Допустим:

  • основной API — https://api.nuxt.com;
  • мы храним JWT-токен в сессии с помощью nuxt-auth-utils;
  • если API отвечает кодом состояния 401, перенаправляем пользователя на страницу /login.
plugins/api.ts
export default defineNuxtPlugin((nuxtApp) => {
  const { session } = useUserSession()

  const api = $fetch.create({
    baseURL: 'https://api.nuxt.com',
    onRequest ({ request, options, error }) {
      if (session.value?.token) {
        // нужен ofetch >= 1.4.0; при необходимости обновите lockfile
        options.headers.set('Authorization', `Bearer ${session.value?.token}`)
      }
    },
    async onResponseError ({ response }) {
      if (response.status === 401) {
        await nuxtApp.runWithContext(() => navigateTo('/login'))
      }
    },
  })

  // Предоставляем в useNuxtApp().$api
  return {
    provide: {
      api,
    },
  }
})

Плагин пробрасывает $api в useNuxtApp() — вызывать API можно прямо из компонентов:

app.vue
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
Обёртка через useAsyncDataснижает риск двойного запроса при SSR (один раз на сервере и согласованная гидратация на клиенте).

Пользовательские useFetch/useAsyncData

Когда логика $api готова, оберните её в композабл useAPI, чтобы не дублировать useAsyncData + $api:

composables/useAPI.ts
import type { UseFetchOptions } from 'nuxt/app'

export function useAPI<T> (
  url: string | (() => string),
  options?: UseFetchOptions<T>,
) {
  return useFetch(url, {
    ...options,
    $fetch: useNuxtApp().$api as typeof $fetch,
  })
}

Пример компонента с новым композаблом:

app.vue
<script setup>
const { data: modules } = await useAPI('/modules')
</script>

Чтобы задать тип возвращаемой ошибки, можно сделать так:

import type { FetchError } from 'ofetch'
import type { UseFetchOptions } from 'nuxt/app'

interface CustomError {
  message: string
  statusCode: number
}

export function useAPI<T> (
  url: string | (() => string),
  options?: UseFetchOptions<T>,
) {
  return useFetch<T, FetchError<CustomError>>(url, {
    ...options,
    $fetch: useNuxtApp().$api,
  })
}
Этот пример демонстрирует использование пользовательского useFetch, но та же структура идентична и для пользовательского useAsyncData.
Прочитайте и отредактируйте живой пример в Docs > 3 X > Examples > Advanced > Use Custom Fetch Composable.
Сейчас обсуждается более удобный способ настройки собственного клиента запросов; см. обсуждение на GitHub.