Vue 3のAPIエラー処理と認証トークンの扱い方|axios interceptor入門

プログラミング

※ 当ブログ「日々是事始め(コレコト)」はプロモーションを含みます。

正常系(データが取れる)は動くのに、いざ「ログインが切れた」「入力が弾かれた」「サーバーが落ちた」となると、とたんに何をどう扱えばいいか分からなくなりました。実務のアプリはむしろエラーと認証の作り込みが本番です。この記事では、API のエラー処理と認証トークンの扱いを、最小構成で整理します。

※ 学習用に調べた内容を個人的にまとめたものです。文章は AI の補助を受けて作成しています。


HTTPエラーの種類と対応の基本

API が返すエラーは、ステータスコードで大まかに意味が決まっています。コード別に対応を決めておくと、画面での扱いがブレません。

コード意味典型的な対応
400 / 422入力が不正フォームにエラー内容を表示
401認証が切れているログイン画面へ誘導
403権限がない「権限がありません」を表示
404対象が見つからない「見つかりません」を表示
500サーバー側のエラー「時間をおいて再試行」を表示

interceptor で共通処理をまとめる

毎回すべての API 呼び出しに try/catch を書くのは大変です。axios の interceptor(横取り)を使うと、全リクエスト・全レスポンスに共通処理を差し込めます。まずは共通の axios インスタンスに設定します。

// shared/api/httpClient.ts
import axios from 'axios'
import { useAuthStore } from '@/stores/authStore'
import { router } from '@/app/router'

export const httpClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
})

// リクエスト時:トークンを付ける
httpClient.interceptors.request.use((config) => {
  const auth = useAuthStore()
  if (auth.token) {
    config.headers.Authorization = `Bearer ${auth.token}`
  }
  return config
})

// レスポンス時:401ならログインへ
httpClient.interceptors.response.use(
  (res) => res,
  (error) => {
    if (error.response?.status === 401) {
      useAuthStore().logout()
      router.push('/login')
    }
    return Promise.reject(error)
  },
)

これで「トークンの付与」と「認証切れ時のログイン誘導」が、全 API 呼び出しに自動で効きます。個別の画面はこれらを気にしなくて済みます。


認証トークンの持ち方

トークンはPiniaのストアに置いて、画面をまたいで共有するのが扱いやすいです。ログイン成功時に受け取ったトークンを保存し、ログアウトで消します。

// stores/authStore.ts(抜粋)
export const useAuthStore = defineStore('auth', () => {
  const token = ref<string | null>(null)
  function setToken(t: string) { token.value = t }
  function logout() { token.value = null }
  return { token, setToken, logout }
})

保存場所には注意が必要です。localStorage に入れるとページを閉じても残って便利な反面、XSS(不正スクリプト)でトークンを盗まれるリスクがあります。セキュリティ要件に応じて、メモリ保持+HttpOnly Cookie などの方式も検討します。学習段階では「トークンは Pinia に持ち、interceptor で付与する」形をまず押さえれば十分です。


画面へのエラー表示

入力エラー(400/422)など画面ごとに見せたいものは、composableerror を返し、テンプレートで表示します。共通処理(認証切れ)は interceptor、画面固有のエラーは composable、と役割を分けると整理できます。

const error = ref<string | null>(null)
try {
  await taskRepository.create(title)
} catch (e) {
  error.value = '登録に失敗しました。入力内容をご確認ください。'
}


もっと体系的に学ぶなら(書籍)

この記事はシリーズの一部です。TypeScript前提で Composition API・Pinia・Vue Router・テストまで一冊で体系的に学びたい方には、内容がそのまま重なる次の本がおすすめです。

次に読む

Vuetifyのフォーム・入力チェック・API送信|v-formとrulesの基本
※ 当ブログ「日々是事始め(コレコト)」はプロモーションを含みます。Vuetify を導入できたら、次は実用の山場である入力フォームです。ただ入力欄を置くだけでなく、「必須なのに空」「文字数オーバー」を弾き、送信中はボタンを押せなくし、サー...
Vue 3を体系的に学ぶロードマップ|初心者がリアクティブ・REST API・Swagger連携・Vuetifyまで理解する手順
※ 当ブログ「日々是事始め(コレコト)」はプロモーションを含みます。Vue 3 を学び始めたとき、私がいちばん困ったのは「何から手をつければいいのか分からない」ことでした。リアクティブ、Composition API、Vue Router、...

PR

Back to top
タイトルとURLをコピーしました