Nuxt Nation カンファレンスが開催されます。11月12〜13日にご参加ください。

Nuxtでのカスタム useFetch

Nuxt 3で外部APIを呼び出すためのカスタムフェッチャーを作成する方法。

Nuxtを使用していると、フロントエンドを作成して外部APIをフェッチすることがあります。その際、APIからのフェッチにいくつかのデフォルトオプションを設定したい場合があります。

$fetchユーティリティ関数(useFetchコンポーザブルで使用)は、意図的にグローバルには設定できません。これは、アプリケーション全体のフェッチ動作の一貫性を保ち、他の統合(モジュールなど)が$fetchのようなコアユーティリティの動作に依存できるようにするために重要です。

ただし、Nuxtには、API(または、呼び出すAPIが複数ある場合は複数のフェッチャー)用のカスタムフェッチャーを作成する方法が用意されています。

カスタム$fetch

Nuxtプラグインを使用して、カスタム$fetchインスタンスを作成してみましょう。

$fetchは、NuxtサーバーのベースURLの追加や、SSR中の直接関数呼び出し(HTTPラウンドトリップの回避)をサポートする、ofetchの設定済みインスタンスです。

ここで、次のことを前提として説明します。

  • メイン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'))
      }
    }
  })

  // Expose to useNuxtApp().$api
  return {
    provide: {
      api
    }
  }
})

このNuxtプラグインを使用すると、Vueコンポーネントから直接API呼び出しを行うために、useNuxtApp()から$apiが公開されます。

app.vue
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
useAsyncDataでラップすると、サーバーサイドレンダリング時(ハイドレーション時のサーバーとクライアント)の二重データフェッチを回避できます。

カスタムuseFetch/useAsyncData

$apiに必要なロジックができたので、useAsyncData + $apiの使用を置き換えるためのuseAPIコンポーザブルを作成しましょう。

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の場合も同じ構造になります。
カスタム$fetchとNuxtでのリポジトリパターンに関するビデオをご覧ください。
現在、カスタムフェッチャーを作成するためのよりクリーンな方法について議論しています。 https://github.com/nuxt/nuxt/issues/14736を参照してください。