Nuxt Nation conference is coming. Join us on November 12-13.

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

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

При работе с Nuxt вы можете делать фронтенд и получать данные из внешнего API, и вам, возможно, захочется установить некоторые параметры по умолчанию для получения данных из вашего API.

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

Однако Nuxt предоставляет возможность создать пользовательский "фетчер" для вашего API (или несколько фетчеров, если у вас есть несколько API для вызова).

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

Давайте создадим пользовательский экземпляр $fetch с помощью плагина Nuxt.

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

Давайте представим, что:

  • Основной 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) {
        // note that this relies on ofetch >= 1.4.0 - you may need to refresh your 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
    }
  }
})

С помощью этого плагина Nuxt, $api выводится из useNuxtApp() для осуществления вызовов API непосредственно из компонентов Vue:

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

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

Теперь, когда $api имеет нужную нам логику, давайте создадим useAPI composable, чтобы заменить использование 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
  })
}

Давайте воспользуемся новым композаблом и получим красивый и чистый компонент:

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

If you want to customize the type of any error returned, you can also do so:

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.
Посмотрите видео о пользовательских $fetch и Паттерне Репозитория в Nuxt.
В настоящее время мы обсуждаем возможность создания более чистого способа, позволяющего вам создавать пользовательский фетчер, см. https://github.com/nuxt/nuxt/issues/14736.