概要
Vue 3 + TypeScript + axiosでAPIへ接続する方法の基礎となる部分をまとめた。
(axiosとはブラウザやnode.js上で動くPromiseベースのHTTPクライアント)
Vue初心者がハマりがちなCORSのエラーについても後述する。
環境
- Vue 3.0.0
- TypeScript 4.1.6
- axios 0.21.1
Install vue/cli
$ yarn global add @vue/cli
Create App
Vueのアプリケーションを作成する。
$ vue create sample-app
TypeScriptとRouterを有効にする。
Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project:
◉ Choose Vue version
◉ Babel
◉ TypeScript
◯ Progressive Web App (PWA) Support
❯◉ Router
◯ Vuex
◯ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
Vueのversionは3.xを選ぶこと。それ以外はお好みで。
Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: (Use arrow keys)
❯ ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
TSLint (deprecated)
axiosをインストール
$ yarn add axios
axiosの共通設定
src/の下にhttp-common.tsという名前のファイルを作成し、axiosを使う際のheadersなどの共通設定を定義する。baseURLにはAPIのURIを定義している。
import axios, { AxiosInstance } from "axios";
const apiClient: AxiosInstance = axios.create({
// APIのURI
baseURL: "http://localhost:3000",
// リクエストヘッダ
headers: {
"Content-type": "application/json",
},
});
export default apiClient;
APIへのエンドポイント
services/SampleApiService.tsというファイルを作成し、APIの各エンドポイントの定義をする。先程作成したhttp-common.tsをimportして使う。
import http from "@/http-common";
class SampleApiService {
getAll(): Promise<any> {
return http.get("/api/books");
}
get(id: any): Promise<any> {
return http.get(`/api/books/${id}`);
}
create(data: any): Promise<any> {
return http.post("/api/books", data);
}
update(id: any, data: any): Promise<any> {
return http.put(`/api/books/${id}`, data);
}
delete(id: any): Promise<any> {
return http.delete(`/api/books/${id}`);
}
deleteAll(): Promise<any> {
return http.delete(`/api/books`);
}
findByDescription(title: string): Promise<any> {
return http.get(`/api/books?title=${title}`);
}
}
export default new SampleApiService();
componenetから利用する
各componentからこんな感じで呼び出して利用できるようになる。
import SampleApiService from "@/services/SampleApiService";
SampleApiService.getAll();
SampleApiService.get(1);
SampleApiService.deleteAll();
CORSのエラー
Vue.jsのサーバーと、APIのサーバーのOriginが異なる場合、APIアクセス時に以下のようなエラーが出ることがある。
Access to XMLHttpRequest at 'http://localhost:3000/api' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
例えば、Vue.jsのサーバーが http://localhost:8080 、APIサーバーが http://localhost:3000 のようにそれぞれのOriginが異なる場合、単純リクエスト(GET, HEAD, POST)以外のリクエスト時にこのエラーが起きる場合がある。
単純リクエスト以外、つまりDELETE、PUTなどのリクエスト時には、リクエスト送信前にpreflightリクエストというものが飛び、そのHTTPメソッドが相手先サーバーで許可されているかどうかの確認を行う。相手先サーバーで許可されていない場合にこのエラーが発生する。
解決策は以下の2つがあるが、2のproxyを使った方法が簡単であり、そちらを取る場合が多いようなので、ここでもproxyの設定方法を説明する。
- 相手先サーバー(今回の場合はAPIサーバー)でOriginの許可設定をする
- Vue.jsサーバーでproxyを利用する
proxyの設定
root階層、package.jsonのある階層にvue.config.jsを作成し、proxyの設定をする。
以下の設定をすることで、devServer(vue-cli-service serve
で動くサーバー)上で、http://localhost:8080 へのアクセスで且つ、URIに/apiを含むリクエストが発行された場合、宛先を http://localhost:3000 に書き換えてアクセスするようになる。
つまり、Vue.jsサーバーがhttp://localhost:8080 、APIサーバーがhttp://localhost:3000の場合、 http://localhost:8080/api/** にアクセスすると、 http://localhost:3000/api/** にリクエストが書き換えられアクセスされる。
これにより、ブラウザ上では http://localhost:8080/api/** へアクセスしていることになるので、CORSのエラーを回避することができる。
module.exports = {
devServer: {
proxy: {
"^/api*": {
target: "http://localhost:3000",
pathRewrite: { "^/api": "" },
changeOrigin: true,
logLevel: "debug",
}
}
}
};
baseURLを削除
http-common.tsのbaseURLがあるとproxyが効かなくなるので削除する。
import axios, { AxiosInstance } from "axios";
const apiClient: AxiosInstance = axios.create({
- // APIのURI
- baseURL: "http://localhost:3000",
// リクエストヘッダ
headers: {
"Content-type": "application/json",
},
});
export default apiClient;
これでCORSのエラーを回避できる。