Chapter 04

Nuxt.js と Rails の連携

Rails と Nuxt.js のそれぞれの環境構築が完了しましたので、次は Nuxt.js から Rails にリクエストを送り、Rails で何らかの処理をして、レスポンスを返し、Nuxt.js でそのレスポンスを受け、表示される処理を実装していきます。

仕様

以下のような仕様を定義し進めていきます。

  • トップページに [タスク取得] ボタンがある
  • [タスク取得] ボタンをクリックすると、タスクが表示される
  • エンドポイント
    • /api/v1/tasks
  • GET メソッドでリクエストを送る

チュートリアル用の簡易的なアプリケーションなので、諸々 雑に 簡略化して作成していますが、そちらについてはご了承ください。

Nuxt.js の実装

Nuxt.js の実装を進めていきます。
docker を用いた開発において、個人的に hostマシンから $ docker-compose run ... というコマンドを叩くよりもそれぞれのコンテナの中に入って npm install ... 等のコマンドを叩く手法で開発しているので、以下そのような形でコマンドを記載していきます。
適宜、 $ docker-compose run ... という形に読み替えていただければと思います。

まずは、 Nuxt.js のデフォルトの index.vue を以下のように修正します。

<template>
  <div class="container">
    <div>
      <h1 class="title">
        Zenn-app
      </h1>
      <h2 class="subtitle">
        {{ subTitle }}
      </h2>
      <button @click="getSomething">
        タスク取得
      </button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      subTitle: 'Zenn is good service!!'
    }
  },
  methods: {
    getSomething() {
      alert("Get Something!!")
    }
  },
}
</script>

こうすることで、 [タスク取得] ボタンが表示され、 クリックするとアラートで「Get Something!!」と表示されるようになるかと思います。
一旦ここまでで動作確認をします。
動作確認で問題なければ、この部分を Rails からタスクを取得する処理に変えていきます。

@nuxtjs/axios をインストールします。

# Nuxt.js のコンテナに入ります
$ docker-compose exec frontend /bin/bash
# @nuxtjs/axios をインストールします
> npm install @nuxtjs/axios@5.12.2

frontend/nuxt.config.jsmodules@nuxtjs/axios を追加します。

...
  modules: [
    '@nuxtjs/axios',
  ],
...

axios を用いて http リクエストを送る処理に変更し、返ってきたレスポンスを tasks の配列にセットするように変更します。
さらに、tasks の配列の title をリスト形式で表示します。

<template>
  <div class="container">
    <div>
      <h1 class="title">
        Zenn-app
      </h1>
      <h2 class="subtitle">
        {{ subTitle }}
      </h2>
      <button @click="getSomething">
        タスク取得
      </button>
      // 取得した tasks をリスト形式で表示する
      <ul v-for=" task in tasks" :key="task.id">
        <li style="text-align: left;">{{ task.title }}</li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      subTitle: 'Zenn is good service!!',
      tasks: []
    }
  },
  methods: {
    async getSomething() {
      // タスク一覧を取得するための API を叩く
      const response = await this.$axios.$get('http://localhost:5000/api/v1/tasks')
      this.tasks = JSON.parse(response.tasks)
    }
  },
}
</script>

Rails の実装

まずは、 CORS を有効にするために、 gem の rack-cors をインストールします。

# Gemfile
gem 'rack-cors'
# Rails のコンテナに入ります
$ docker-compose exec backend /bin/bash
> bundle intall

bundle install をしたのちに、 config/initializers/cors.rb を編集します。

# zenn-app/backend/config/initializers/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    # localhost:3000 からのアクセスを許容する
    origins ['http://localhost:3000']

    resource '*',
    headers: :any,
    methods: %i[get post put patch delete options head]
  end
end

次に、Task の model と controller, ルーティングの設定を行います。

# Rails のコンテナの中
> bin/rails g model Task

migration ファイルの設定をします。

class CreateTasks < ActiveRecord::Migration[6.0]
  def change
    create_table :tasks do |t|
      t.string :title, null: false
      t.timestamps
    end
  end
end

mimgration を流します。

> bin/rails db:migrate

次に、Task モデルにバリデーションの設定を追加します。

class Task < ApplicationRecord
  validates :title, presence: true
end

続いて、controller の設定、ルーティングの設定を行います。

# zenn-app/backend/app/controllers/api/v1/tasks_controller.rb
module Api
  module V1
    class TasksController < ApplicationController
      def index
        # この辺りはかなり簡略化しています。
	# 本来であれば seeds.rb を作成した方が良いです...
        if Task.count.zero?
          ['1st task', '2nd task', '3rd task'].each do |title|
            Task.create!(title: title)
          end
        end
	# この辺りも本来であればきちんとシリアライズした方が良いです...
        render json: { tasks: Task.all.to_json }
      end
    end
  end
end
# zenn-app/backend/config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :tasks, only: :index
    end
  end
end

動作確認

ここまで実装したら、 http://localhost:3000/ でアプリケーションのトップ画面を開き、 [タスク取得] ボタンをクリックして、タスクが表示されるかを確認します。

以下のように表示されれば OK です。