※ 当ブログ「日々是事始め(コレコト)」はプロモーションを含みます。
正常系(データが取れる)は動くのに、いざ「ログインが切れた」「入力が弾かれた」「サーバーが落ちた」となると、とたんに何をどう扱えばいいか分からなくなりました。実務のアプリはむしろエラーと認証の作り込みが本番です。この記事では、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)など画面ごとに見せたいものは、composableで error を返し、テンプレートで表示します。共通処理(認証切れ)は interceptor、画面固有のエラーは composable、と役割を分けると整理できます。
const error = ref<string | null>(null)
try {
await taskRepository.create(title)
} catch (e) {
error.value = '登録に失敗しました。入力内容をご確認ください。'
}
もっと体系的に学ぶなら(書籍)
この記事はシリーズの一部です。TypeScript前提で Composition API・Pinia・Vue Router・テストまで一冊で体系的に学びたい方には、内容がそのまま重なる次の本がおすすめです。
PR
📕 紙の本
Vue 3 フロントエンド開発の教科書 [ WINGSプロジェクト 齊藤 新三 ] 価格:3960円 |
📱 電子書籍版
Vue 3 フロントエンド開発の教科書 【電子書籍】[ WINGSプロジェクト 齊藤新三【著】 ] 価格:3960円 |
次に読む




