Nuxt3とAxiosで認証付きAPIリクエストを実現する方法☝️composablesを使いこなしましょう😎

Nuxt3とAxiosで認証付きAPIリクエストを実現する方法☝️composablesを使いこなしましょう😎

こんにちは!

Nuxt3がリリースされてだいぶ日が経ちました、私たちもインハウスのプロダクトでも愛用するフロントエンドのフレームワークです。本記事では、フロントエンドの開発で必須な認証 API へのリクエストを Nuxt3と Axios で実現する方法を解説します。

想定する読者

  • Nuxt3で開発しているヒト
  • Axios で API リクエストを実装しているヒト

はじめに

Nuxt3の composables とは

composables ディレクトリにファイルを設置すると、Vue の実装時に自動インポートできます。この機能を応用すると、本記事でも紹介しますが、use関数を自前で作成することが可能となります。

具体的な使用方法は以下となります。

// composables/useFoo.ts
export const useFoo = () => {
  return useState('foo', () => 'bar')
}

以下のような default export のパターンでも実装可能です。好みの方法で実装してください。

// composables/use-foo.ts or composables/useFoo.ts
export default function () {
  return useState('foo', () => 'bar')
}

上記は以下のように使用します。

<script setup lang="ts">
const foo = useFoo()
</script>

<template>
  <div>
    {{ foo }}
  </div>
</template>

Axios で認証付き API リクエストを実装

リポジトリ層に相当する API リクエストファイルを自動生成

swagger-typescript-apiモジュールを使用し、Swagger ファイルから API リクエスト情報を扱うファイルを自動生成します。私たちも RestAPI 開発時は使用するモジュールです。具体的な実行コマンドは以下になります。

$ yarn add swagger-typescript-api -D
$ npx swagger-typescript-api \
  -p ./swagger.yaml \
  -o ./types \
  -n API.ts \
  --axios \
  --sort-types \
  --sort-routes \
  --extract-request-params \
  --extract-request-body \
  --extract-response-body \
  --extract-response-error \
  --extract-enums \
  --type-prefix "API"

細かい引数は好みで調整してください。本記事のテーマではないため細部の解説は行いません。上記で生成されたAPI.ts ファイルは、後述の composables/useRepository.ts ファイル内で使用します。

composables/useRepository.ts の実装

Content-type ヘッダーはマルチパートフォーム等々に変更する可能性が多々ありますので、useRepository 呼び出し時に引数 axiosConfig で変更できるようにしています。

// composables/useRepository.ts

import { Api as API } from 'types/API'
import _ from 'lodash'
import type { AxiosRequestHeaders, CreateAxiosDefaults } from 'axios'

export default async (opt?: { axiosConfig?: CreateAxiosDefaults }) => {
  const runtimeConfig = useRuntimeConfig()
  const authenticationService = useAuthenticateService()

  const optContentType: string | undefined = _.get(
    opt,
    "axiosConfig.headers.['Content-Type']",
  )

  const axios = new API(
    _.assign(
      {
        baseURL: runtimeConfig.public.REST_API_DOMEIN,
        timeout: _.toNumber(runtimeConfig.public.REST_API_TIMEOUT),
        maxRedirects: _.toNumber(runtimeConfig.public.REST_API_MAX_RETRIES),
        headers: {
          'Content-Type': optContentType ? optContentType : 'application/json',
        },
      },
      (opt?.axiosConfig || {}) as Record<string, unknown>,
    ),
  )

  axios.instance.interceptors.request.use(
    async (config) => {
      try {
        // getCurrentUser() 実行時にリフレッシュトークンを用いた認証トークンの更新が適宜行われる
        const currentSession = await authenticationService.getCurrentUser()
        return {
          ...config,
          headers: {
            ...(config.headers || {}),
            Authorization: `Bearer ${currentSession}`,
          } as AxiosRequestHeaders,
        }
      } catch (e) {
        await authenticationService.signOut()
        window.location.href = '/login'
      }
      return config
    },
    (error) => {
      return Promise.reject(error)
    },
  )

  return axios
}
// pages/foo.vue

<script setup lang="ts">
const repo = await useRepository()
</script>

<template>
  <div>
    {{ foo }}
  </div>
</template>

Axios のインスタンスを作成する際の細かい引数(ドメインやタイムアウト時のリトライ)については、nuxt.config.ts 内に環境変数で指定できるようにしています。プロジェクトによって要件が様々なためです。プロジェクトに応じて柔軟に変更してみてください!

まとめ

Nuxt3の composables を使用すると、愚直に services 層や utils 層へ処理を実装するよりも、プロジェクト全体で汎用的で使いやすい処理を実装可能です。

AWSサーバーレスフロントエンドの開発はお気軽にお問い合わせください。