Programming Journal

学習したことの整理用です。

【Vue.js】axiosを利用してAPIを使用する

実装したいこと

タスク管理アプリを作成しています。
APIからタスクのデータを取得して、表示させたいです。

基本的に公式の流れに沿って進めていきます。
axios を利用した API の使用 — Vue.js

また、ここまでは実装済です。
study-diary.hatenadiary.jp

使用バージョン

実装の流れ

大きな流れは、「タスク一覧を取得するAPIを生成→Vue.jsで取得」です。
Task modelは作成済です。

API作成

  • Tasks controllerを作成
  • routing設定
  • CSRF無効化

APIの呼び出し

  • axiosインストール
  • Vueファイルの設定

そもそも、APIって?

API is the acronym for Application Programming Interface, which is a software intermediary that allows two applications to talk to each other. Each time you use an app like Facebook, send an instant message, or check the weather on your phone, you’re using an API.
What is an API? (Application Programming Interface) | MuleSoft

例えば、Expediaみたいな航空券予約サービスはエアラインAPIを使っていて、顧客のリクエストを受けて各社の最新の情報を提供する。

RailsでもAPIモードがあるので、APIを作成できると。
今回はこっち。恥ずかしながらRailsAPIを生成できるのを知らなかった Railsで超簡単API - Qiita

Tasks controller

$ bundle exec rails g controller Api::Tasks index show create update destroy --skip-routes
class Api::TasksController < ApplicationController
  before_action :set_task, only: %i[show update destroy]

  def index
    @tasks = Task.all
    render json: @tasks
  end

  def show
    render json: @task
  end

  def create
    @task = Task.new(task_params)

    if @task.save
      render json: @task
    else
      render json: @task.errors, status: :bad_request
    end
  end

  def update
    if @task.update(task_params)
      render json: @task
    else
      render json: @task.errors, status: :bad_request
    end
  end

  def destroy
    @task.destroy!
    render json: @task
  end

  private

  def set_task
    @task = Task.find(params[:id])
  end

  def task_params
    params.require(:task).permit(:title)
  end
end

Routing

Rails.application.routes.draw do
  root to: 'home#index'

  namespace :api, format: 'json' do
    resources :tasks
  end

  get '*path', to: 'home#index'
end

namespace忘れでエラー発生

この後、curlコマンドでtaskを生成したのですが成功しませんでした。
大量のエラーメッセージが出て、VSCodeのターミナル画面ではカットされてしまったので、MacのTarminalでエラーメッセージを読むと、以下のようにrouting errorでした。
いつものように、resources :tasksとしていたのが原因でした。上記のようにname spaceが必要でした。

<header>
  <h1>Routing Error</h1>
</header>
<div id="container">
  <h2>No route matches [POST] &quot;/api/tasks&quot;</h2>

CSRF対策無効化

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session # 追加
end

APIでは外部から叩かれるのでこの設定はオフにしておきます。

Vue.jsでAPIを使用する

ウェブアプリケーションを構築するとき、 API からデータを取得して表示することがよくあります。これを行うにはいくつかの方法があり、一般的なアプローチは Promise ベースの HTTP クライアントの axios を使うことです。 axios を利用した API の使用 — Vue.js

axiosインストール

$ yarn add axios

GitHub - axios/axios: Promise based HTTP client for the browser and node.js

APIから取得したデータを表示(基本構文)

axios
  .get('http://localhost:3000/api/tasks')
  .then(response => (this.tasks = response.data))
  .catch(error => console.log(error)) //エラー処理。console.logでエラー表示

axios設定

import axios from 'axios'

const axiosInstance = axios.create({ baseURL: 'api' });

export default axiosInstance //生成したインスタンスをexport

baseURL will be prepended to url unless url is absolute. It can be convenient to set baseURL for an instance of axios to pass relative URLs to methods of that instance. GitHub - axios/axios: Promise based HTTP client for the browser and node.js

baseURLは不完全なurlurlを補完してくれる機能。


import Vue from 'vue'
import App from '../app.vue'
import router from '../router'
// さっき作ったaxiosインスタンスをimportする
import axios from '../plugins/axios'
import 'bootstrap/dist/css/bootstrap.css'

Vue.config.productionTip = false
// importしたaxiosをprototypeに追加する
Vue.prototype.$axios = axios

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    router,
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)
})

インスタンスプロパティの追加 — Vue.js

プロトタイプに追加すれば、すべてのVueインスタンスで、this.$axios.getのように簡単に使うことができる。

Vueファイル設定

<template>
  <div v-for="task in tasks" :key="task.id"> //配列に入ったtasksを1つずつ表示
    <span>{{ task.title }}</span>
  </div>
</template>

<script>
// (先述)/packs/hello_vue.jsにprototypeを設定しているので、importしなくてOK。
// (先述)各所でthis.$axios.get~~~のようにaxiosを使うことができるようになる。

export default {
  name: "TaskIndex",
  data() {
    return {
      tasks: [] //空の配列を用意する。受け取ったデータが入る
    }
  },
  created() { //インスタンス生成時にフック
    this.fetchTasks(); //fetchTasksメソッドを呼び出し
  },
  methods: {
    fetchTasks() {
      this.$axios.get("tasks") //baseURLオプションを登録しているのでこのように省略して書ける
      .then(response => (this.tasks = response.data)) //取得したデータをtasksに代入
      .catch(error => console.log(error.status));
    }
  }
}
</script>

<style scoped>
</style>

最初自分の実装では、このファイルにimport axios from 'axios'とか全部書いていたのですが、 hello_vue.js`に設定しておくことで一々呼び出さずに済みます。

curl コマンド

ターミナル上でタスクの作成〜削除まできます。

$ curl -X POST -H "Content-Type: application/json" -d '{"title":"タスクの作成"}' localhost:3000/api/tasks
$ curl -X DELETE  localhost:3000/api/tasks/(該当のID)
$ curl http://localhost:3000/api/tasks