画面に表示するデータを、APIサーバーから取得する場面は、Webアプリで最もよくある処理の1つです。VueではHTTPクライアントのAxiosを使うと、少ないコードで書けます。この記事では、Vue 3(<script setup>)を前提に、APIからJSONを取得して表示する基本の形と、実際に必要になる、エラー処理やタイムアウト、中止までを整理します。
準備:プロジェクトの作成とAxiosのインストール
新しく始めるなら、Vueの公式ツールcreate-vue(npm create vue@latest)を使います。Viteをベースにした、現在の標準のプロジェクト構成になります。Vue CLIで作られた古いプロジェクトでも、このあとのコードは同じように使えます。
# プロジェクトを作る(Vite ベースの公式ツール) npm create vue@latest # Axios を追加する npm install axios
基本の取得と表示
onMountedの中でaxios.get()を呼び、結果をrefに入れて、v-forで表示します。取得したJSONは、response.dataに入ります。読み込みの状態はloading、失敗はerrorで持ちます。loadingを最初からtrueにしておくと、取得前に空のリストが一瞬見えることがありません。finallyでfalseに戻すと、成功しても失敗しても、読み込みの終了を扱えます。
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const posts = ref([])
const loading = ref(true) // 最初から「読み込み中」にしておく
const error = ref('')
onMounted(async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts')
posts.value = response.data.slice(0, 10) // JSON は response.data に入る
} catch (e) {
error.value = '取得に失敗しました'
} finally {
loading.value = false // 成功しても失敗しても、読み込みは終わり
}
})
</script>
<template>
<p v-if="loading">読み込み中…</p>
<p v-else-if="error">エラー:{{ error }}</p>
<ul v-else>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
AxiosはHTTPのステータスが2xx以外のとき、自動的にエラー(reject)として扱います。そのため、fetchのように、response.okを自分で確認する必要はありません。fetchとの違いはJSONファイルからデータを取得する方法をご覧ください。リスト表示はv-forで配列やオブジェクトをループして表示する方法、表示の出し分けはv-ifで条件分岐させる方法で解説しています。
共通の設定をまとめる:axios.create
URLの前半(baseURL)やtimeoutのような共通の設定は、axios.createでまとめたインスタンスを作ります。特にtimeoutは重要です。Axiosは、既定ではタイムアウトがなく、サーバーから応答が返らないと、「読み込み中」のまま止まってしまいます。
// api/client.js:共通の設定をまとめた Axios のインスタンス
import axios from 'axios'
export const api = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com', // 毎回書かなくてよくなる
timeout: 10000 // 10秒で諦める(既定はタイムアウトなし)
})
エラーの種類を見分ける
Axiosのエラーは、原因によって、持っている情報が違います。大きく3つに分けて扱うと、利用者に伝えるメッセージを変えられます。
error.responseがある:サーバーが応答したが、エラーのステータスだった(404、500など)。error.requestだけがある:リクエストは出したが、応答がなかった。ネットワークの断絶、タイムアウト、後述のCORSなど。- どちらもない:リクエストを作る前の、設定のミスなど。
error.messageを確認する。
取得を中止する:AbortController
取得中に別の画面へ移動すると、古いリクエストの応答が、あとから返ってきて処理されます。AbortControllerのsignalをリクエストに渡しておき、画面を離れるときにabort()すると、中止できます。自分で中止したときは、エラーとして扱わないよう、axios.isCancel(e)で見分けます。連続して取得するときの古い応答の上書きなど、さらに詳しい扱いは非同期APIの状態管理パターンをご覧ください。
完成コード
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import axios from 'axios'
import { api } from './api/client'
const posts = ref([])
const loading = ref(true)
const error = ref('')
// エラーの種類に応じて、メッセージを変える
function toMessage(e) {
if (e.response) {
// サーバーが応答したが、エラーのステータスだった
return `サーバーがエラーを返しました(${e.response.status})`
}
if (e.request) {
// リクエストは出したが、応答がなかった(ネットワーク断、タイムアウト、CORS など)
return '通信できませんでした。ネットワークを確認してください。'
}
// リクエストを作る前の、設定のミスなど
return `リクエストに失敗しました:${e.message}`
}
let controller = null
onMounted(async () => {
controller = new AbortController()
try {
const response = await api.get('/posts', { signal: controller.signal })
posts.value = response.data.slice(0, 10)
} catch (e) {
if (axios.isCancel(e)) return // 自分で中止した場合は、エラー扱いにしない
error.value = toMessage(e)
} finally {
loading.value = false
}
})
// 画面を離れるときは、取得中のリクエストを中止する
onBeforeUnmount(() => controller?.abort())
</script>
<template>
<p v-if="loading">読み込み中…</p>
<p v-else-if="error" role="alert">{{ error }}</p>
<ul v-else>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
エラーを画面にどう見せるかの設計はAPIエラーハンドリングの設計例、レスポンスに応じた画面の出し分けはAPIレスポンスに応じたUI分岐処理の書き方が参考になります。状態をまとめたComposableにしたい場合は、非同期APIの状態管理パターンをご覧ください。
つまずきやすい点
CORSのエラー
外部のAPIを呼ぶと、ブラウザのコンソールに「CORS」のエラーが出て、取得できないことがあります。MDNによると、ブラウザは、別のオリジン(ドメイン、スキーム、ポートのどれかが違うもの)へのリクエストを、サーバーがAccess-Control-Allow-Originのヘッダーで許可した場合にだけ許します。失敗した詳しい理由は、セキュリティ上、JavaScriptからは見えず、ブラウザのコンソールでしか確認できません。クライアント側のコードだけでは直せず、APIのサーバー側で許可するか、自分のサーバーを経由して呼び出す必要があります。
APIキーなどの秘密情報を、フロントのコードに書かない
ブラウザに送られるコードは、誰でも読めます。環境変数に入れても、ビルドに埋め込まれるものは、公開されたのと同じです。秘密のキーが必要なAPIは、自分のサーバーを経由して呼び出してください。環境ごとの接続先の切り替えは環境ごとにAPIのURLを切り替える方法をご覧ください。
よくある質問(FAQ)
response.dataに自動で入る、2xx以外が自動でエラーになる、baseURLやtimeoutなどを共通設定にまとめやすい、といった点です。単純な取得なら、追加のインストールが不要なfetchでも十分です。timeoutを指定し、finallyでloadingをfalseに戻してください。Access-Control-Allow-Originで自分のオリジンを許可するか、自分のサーバーを経由してAPIを呼び出す必要があります。AbortControllerで、画面を離れるときに、リクエストを中止してください。まとめ
Axiosでの取得は、onMountedとasync/await、loadingとerrorの状態で基本の形になります。実用にするには、timeoutを設定し、エラーの種類を見分け、画面を離れるときには取得を中止します。CORSのエラーと、秘密情報をフロントに置かない点にも注意してください。

