7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ruby on RailsAdvent Calendar 2022

Day 18

Railsで必要十分なAPI開発環境を整える(Docker + Rails + MySQL + OpenAPI + CircleCI)

Last updated at Posted at 2022-12-18

はじめに

RailsでAPIを作るぞ!ってなった時にいつも行っているテストやリンターなどの導入手順を備忘録がてら記事にしました。

また、本記事ではRailsアプリケーション自体は最小限の構成で作成し、
なるべく小規模かつ開発に必要なツールは最低限導入しておくことを念頭においています。

完成系

最初に完成系のrepositoryを上げておきます。
実際の実装が見たい場合は下記repositoryをご参照ください。

本記事で扱うこと

  • dockerでmysqlをdbとする最小限のrailsアプリケーションを作成
  • ヘルスチェック用endpointの実装
  • テストツール(rspec)の導入
  • リンターツール(rubocop)の導入
  • API定義書、並びに関連ツール(OpenAPI, Swagger Editor, Swagger UI, committee)の導入
  • CI(CircleCI)の導入
  • CORSの設定
  • JSON用テンプレートエンジン(jbuilder)の導入

本記事で扱わないこと

  • dockerそのもののセットアップ
  • 各種ツールの詳細な説明
  • ヘルスチェック以外のendpointの実装
  • FEアプリケーションの実装
  • 本番環境へのデプロイ
  • CDの実装

Railsアプリケーションの雛形の準備

dockeでRails, Mysqlを使用するための準備

API用のディレクトリと各種ファイルの作成

mkdir /minimal_api
cd /minimal_api

# docker関連のファイルを作成
touch {Dockerfile,entrypoint.sh,docker-compose.yml}

# gem関連のファイルを作成
touch {Gemfile,Gemfile.lock}

Docker関連のファイルを編集

公式のテンプレートを参考にDockerfile, docker-compose.yml, entrypoint.shを作成します。

Dockerfile
FROM ruby:3.1.3

RUN apt-get update -qq && apt-get install -y vim

RUN mkdir /app
WORKDIR /app

ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app

# For rails credentials:edit
ENV EDITOR="vim"

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

ほとんどテンプレートと一緒ですが、今回はAPIの作成であることと、postgresではなくmysqlを使用することから、
node.jsもpostgres-clientも不要です。
また、rails credentialsを使用する時のために、vimをインストールし、環境変数のEDITORに"vim"をセットしておきます。

entrypoint.sh
#!/bin/bash
set -e

rm -f /app/tmp/pids/server.pid

exec "$@"

entrypoint.shは特に変更していません。

docker-compose.yml
version: '3.5'

services:
  db:
    image: mysql:8.0
    ports:
      - 3306:3306
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: passw0rd
      MYSQL_DATABASE: root
    volumes:
      - ./mysql_data:/var/lib/mysql

  app:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app
      - bundle_data:/usr/local/bundle
    ports: 
      - 3001:3000
    depends_on:
      - db
    tty: true
    stdin_open: true
volumes:
  mysql_data:
  bundle_data:

dbには、postgresqlではなくmysqlを使用しています。
appの方には、

tty: true
stdin_open: true

を指定し、コンテナの標準出力/標準入力に接続できるようにしています。
この設定はdebugする際に必要です。
また、mysqlのデータとinstall済みのgemのデータを永続化するため、mysql_dataとbundle_dataをvolumesに指定しています。
./mysql_data:/var/lib/mysql
はコンテナ内の/var/lib/mysqlをホストマシン側の./mysql_dataにマウントしています。
コンテナ内の/var/lib/mysqlにはmysqlのデータが格納されており、それを/mysql_dataにマウントすることで、コンテナを再起動したときでも、ホスト側の/mysql_dataにはコンテナを落とす前のデータが格納されているので、データが初期化されずにすみます。
逆にこれがないとコンテナを落とすたびにデータが消えるので注意してください。

また
bundle_data:/usr/local/bundle
もmysql_data同様、コンテナ内の/usr_local/bundleをホストマシン側のbundle_dataにマウントしています。
usr/local/bundleにはbundle install済みのgemの情報が格納されています。
これがないと、新しくgemを追加するたびにdocker-compose buildする必要があります。
こちらは必須というわけではないですが、あった方が開発上便利なので記載しています。

Gemfileの修正

Gemfile
source "https://rubygems.org"

gem "rails", "~> 7.0.4"

Gemfileではgemのソースを記載し、railsをinstallします。
今回はrailsのv7.0.4を使用します。

※ Gemfile.lockは空のままで大丈夫です。

ビルドする

これで準備は整ったので下記のコマンドでビルドしましょう。

docker-compose build

rails new

さて、ビルドがうまくいったら今度はrails newでアプリの雛形を作成します。

docker-compose run app rails new . --minimal --api -T -d=mysql

これを実行すると、カレンとディレクトリにRailsアプリケーションが作成されます。
ざっくり各オプションの解説をします。

--minimal

Rails 6.1で追加されたオプションで、
ActionCableやActionMailerどのRails特有の機能の生成を全てスキップして、
最小限の構成でRailsアプリケーションを生成できるオプションです。
もちろん必須ではないですが、一応必要十分というテーマなので今回はこのオプションを使用しています。

具体的にskipの対象になるものを知りたい方は下記をご参照ください。

--api

RailsをAPIモードで生成するためのオプションです。
これを使用するとAPIに必要な機能のみを使用するようにRails側でよしなに設定してくれます。
具体的には、

  1. アプリケーション起動時にAPIに不要なmiddle wareを読み込まないようにする
  2. ApplicationControllerがActionController::Baseではなく、ActionController::APIを継承する
  3. views, helpers, assertsを生成しないようにgeneratorを設定する

こちらも、詳細は下記をご参照ください。

-T

テスト関連のファイルの生成をスキップするためのオプションです。
Railsの標準ではminitestが採用されていますが、今回はrspecを使用するのでオプションでこれらの生成をスキップしています。

-d=mysql

使用するデータベースを指定するオプションです。
defaultではsqlite3が指定されていますが、今回はmysqlを使用するのでオプションで指定しています。

gitignoreに/mysql_dataを追記する

docker-compose.ymlでvolumesに指定しているmysql_dataをgit管理対象外にするためにgitignoreに追加します。

...

# Ignore volumed mysql_data # <= 追記(文言はお好みで)
/mysql_data # <= 追記

database.ymlを修正する

作成したmysqlサーバーに接続するため、
docker-compose.ymlで設定した内容通りに修正します。

...

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: minimal_api_dev
  username: root
  password: passw0rd
  host: db

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: minimal_api_test
  username: root
  password: passw0rd
  host: db

...

データベースの作成とマイグレーション

設定ができたらデータベースの作成とマイグレーションを行います。
docker runコマンドを使用しても良いですが、今回はdocker execを使用して、appコンテナ内部に入り、コマンドを実行します。

docker-compose exec app bash
(コンテナ内部) rails db:create db:migrate

http://localhost:3001 にアクセス

http://localhost:3001 にアクセスして、下記のような画面が表示されれば成功です。
スクリーンショット 2022-12-11 22.19.35.png

開発ツール系の導入

この章ではテストツール、リンター、API定義書など開発を進めていく上で必要になってくるツールを導入します。

rspecの導入

rails-rspecのインストール

前述の通り今回は、テストツールとしてrspecを導入します。

Gemfileにrails-rspecを追記します。

Gemfile
...

group :development, :test do
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
  gem 'rspec-rails', '~> 6.0.0' # <= 追記
end

...

Gemfileを追記したらbundle installを実行しましょう、

再度

docker-compose exec app bash

でコンテナ内に入り、

bundle install

を実行します。
rspecがinstallできたら、同じくコンテナ内で、

rails g rspec:install

を実行します。すると下記4つのファイルが作成されます

  • .rspec
  • spec
  • spec/spec_helper.rb
  • rspec/rails_helper.rb

これでrspecの準備は整いました。

rspecを実行してみる

実際にrspecのテストを実装するためにヘルスチェック用のendpoint(GET /api/v1/health_checks)を作成します。
このエンドポイントにリクエストを送ると
{ "result": "ok" }
というJSON形式のレスポンスが返ってくることを期待します。

docker-compose exec app bash

でコンテナ内に入り、

rails g controller api/v1/healthchecks

を実行すると、healthchecks_controller.rb, healthchecks_spec.rbが下記のように作成されます。

app/controllers/api/v1/healthchecks_controller.rb
class Api::V1::HealthchecksController < ApplicationController
end
spec/requests/api/v1/healthchecks_spec.rb
require 'rails_helper'

RSpec.describe "Api::V1::Healthchecks", type: :request do
  describe "GET /index" do
    pending "add some examples (or delete) #{__FILE__}"
  end
end

まずは、healthchecks_spec.rbに期待する結果を記載します。

spec/requests/api/v1/healthchecks_spec.rb
require 'rails_helper'

RSpec.describe "Api::V1::Healthchecks", type: :request do
  describe "GET /api/v1/healthchecks" do
    subject(:send_request) { get "/api/v1/healthchecks" }

    it "returns http success" do
      expect(send_request).to eq 200
      expect(JSON.parse(response.body, symbolize_names: true)).to match({ result: 'ok' })
    end
  end
end

シンプルなテストなので、特に説明は不要だと思いますが、
symbolize_namesのところだけ少し、補足します。
symbolize_namesはJSON.#parseのoptionでこれを指定するとparse結果として出力するhashの引数をシンボルで返してくれます。
これを使用しない場合は、該当行を下記のように書き換える必要があります。

expect(JSON.parse(response.body)).to match({ "result" => 'ok' })

正直使用するかしないかは好みだと思いますが、いちいち""で囲ったり=>で指定したりするのが少々面倒なので、筆者はsymbolize_namesを指定することが多いです。

再度コンテナ内に入り、rspecを実行してみます。

rspec

すると、まだ何も実装していないので当然エラーがでます。

ActionController::RoutingError:
       No route matches [GET] "/api/v1/healthchecks"

これを期待するendpointにするため、healthchecks_controller.rbとroutes.rbを下記のように修正します。

app/controllers/api/v2/healthchecks_controller.rb
class Api::V1::HealthchecksController < ApplicationController
  def index
    render json: { result: 'ok' }
  end
end
config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :healthchecks, only: %i(index)
    end
  end
end

修正が完了したら、再度rspecを実行してみましょう

rspec
# => 1 example, 0 failures

今度は成功するはずです。
これでrspecの導入と最初のテストの実装が完了しました!

rubocopの導入

rspecの導入が終わったので、今度はlinterであるrubocopの導入をします。
Gemfileに下記を追記します。

Gemfile
group :development do
  gem 'rubocop', require: false #<= 追記
  gem 'rubocop-rails', require: false #<= 追記
  gem 'rubocop-rspec', require: false #<= 追記
end

rubocop-railsrubocop-rspecはそれぞれrails, rspec向けのrubocop拡張機能を提供してくれるgemです。

Gemfileの修正が終わったら例のごとくbundle installします。

bundle install

gemのinstallが終わったら下記を実行します。

rubocop --auto-gen-config

すると

  • .rubocop_todo.yml
  • .rubocop.yml
    が生成されます。

本記事ではデフォルトの設定のまま進めますが、結構厳し目なので適宜お好みで設定してください。
それでは、rubocopを実行してみましょう。

rubocop

大量の警告が出たと思います。
今度は-a or -Aオプションをつけてオートコレクト機能を使いましょう。

rubocop -a
or
rubocop -A

一気にコードの修正が行われたと思います。
再度オプションを外してrubocopを実行しましょう。

rubocop

警告が出なくなれば成功です。
これでrubocopの導入が完了しました!

OpenAPI(Swagger)の導入

APIの実装を進めるにあたってAPI定義書を作成するためOpenAPI(Swagger)の導入をしたいと思います。
docker-compose.ymlを下記の内容を追記します。

docker-compose.yml
version: '3.5'

services:
  ...
  swagger-editor:
    image: swaggerapi/swagger-editor
    ports:
      - "8001:8080"
    volumes:
      - ./docs/openapi.yaml:/tmp/openapi.yaml
    environment:
      SWAGGER_FILE: /tmp/openapi.yaml

  swagger-ui:
    image: swaggerapi/swagger-ui
    ports:
      - "8002:8080"
    volumes:
      - ./docs/openapi.yaml:/usr/share/nginx/html/openapi.yaml
    environment:
      API_URL: openapi.yaml

...

swagger-editorはswagger.ymlをブラウザ上で編集するためのツールです。
volumesで、minimal_api/docs/openapi.yamlをコンテナ内の/tmp/openapi.yamlにマウントし、
SWAGGER_FILEで/tmp/openapi.yamlを指定することで、ホストマシン上にあるdocs/openapi.yamlをベースに編集を行うことができます。docs/openapi.yamlは後ほど作成します。

swagger-uiはswagger.ymlをブラウザ上でみやすい形に表示してくれるためのツールです。こちらも同様にdocs/openapi.yamlを閲覧できるようにvolumeとAPI_URLを指定しています。

そして、docsディレクトリを作成し、そこにopenapi.yamlを配置します。

docs/openapi.yaml
openapi: 3.0.3
info:
  title: minimal_api
  version: 1.0.0
servers:
  - url: http://localhost:3001
tags:
  - name: HealthChecks
    description: Endpoint to do healthchecks minimal_api
paths:
  /api/v1/healthchecks:
    get:
      tags:
        - HealthChecks
      summary: get success response
      operationId: healthCheck
      responses:
        '200':
          description: return result ok
          content:
            application/json:
              schema:
                required:
                  - result
                type: object
                properties:
                  result:
                    type: string
                    example: ok

準備が終わったらbuildとupを行います。

docker-compose build
docker-compose up

http://localhost:8001, http://localhost:8002 にアクセスしてみて、下記のように表示されたら成功です。

スクリーンショット 2022-12-17 15.47.44.png

スクリーンショット 2022-12-17 15.47.52.png

Commiteeの導入

committeeはopenAPI等を使う際に便利な機能を提供してくれるMiddlewareです。
今回はrequest specでopenAPIに定義したスキーマに則ったレスポンスが返却されているかどうかのテストを行う為に使用します。

まずは、Gemfileにcommitee, commitee-railsを追記し、bundle installを実行します。

Gemfile

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: %i[mri mingw x64_mingw]
  gem 'committee' # <= 追記
  gem 'committee-rails' # <= 追記
  gem 'rspec-rails', '~> 4.0.1'
end
bundle install

次にrails_helper.rbにcommittee用の設定を追記します。

spec/rails_helper.rb
...

RSpec.configure do |config|
  ...

  # setting for committee
  config.add_setting :committee_options
  config.committee_options = {
    schema_path: Rails.root.join('docs/openapi.yaml').to_s,
    parse_response_by_content_type: false,
    query_hash_key: 'rack.request.query_hash'
  }

  config.include Committee::Rails::Test::Methods
end

GET /api/v1/healthchecksのテストケースにスキーマチェックを追加します。

spec/requests/api/v1/healthchecks_spec.rb
...

RSpec.describe 'Api::V1::Healthchecks', type: :request do
  describe 'GET /api/v1/healthchecks' do
    subject(:send_request) { get '/api/v1/healthchecks' }

    ...

    it 'returns correct response' do
      send_request
      assert_response_schema_confirm(200)
    end
  end
end

assert_schema_conform(200)というところで、
GET /api/v1/healthchecksのステータス200に定義されているスキーマと、
send_requestで取得したresponse.bodyのスキーマが一致しているかを確認しています。

確認のために、一回わざとスキーマを変更してみましょう。

app/controllers/api/v1/healthchecks_controller.rb
module Api
  module V1
    class HealthchecksController < ApplicationController
      def index
        render json: { hoge: 'ok' }
        # render json: { result: 'ok' }
      end
    end
  end
end

テストを実行します。

rspec

すると、新しく追加したテストケースが失敗し、下記のようにエラーが表示されます。

Failure/Error: assert_schema_conform(200)
     
     Committee::InvalidResponse:
       #/paths/~1api~1v1~1healthchecks/get/responses/200/content/application~1json/schema missing required parameters: result

requiredに設定しているパラメータがないよ。
パラメータの名前はresultだよ。

と教えてくれています。
healthchecks_controllerを元に戻して、再度テストを実行します。

app/controllers/api/v1/healthchecks_controller.rb
# frozen_string_literal: true

module Api
  module V1
    class HealthchecksController < ApplicationController
      def index
        render json: { result: 'ok' }
      end
    end
  end
end
rspec

今度は成功したはずです。
これでcommitteを使用して、API定義書通りに実装されているということをテストできるようになりました!

commiteeによって検証が行われるのは、requiredに設定したパラメータのみです。
試しに、required部分をコメントアウトしてみましょう。

docs/openapi.yaml
openapi: 3.0.3
info:
  title: minimal_api
  version: 1.0.0
servers:
  - url: https://localhost:3001
tags:
  - name: HealthChecks
    description: Endpoint to do healthchecks minimal_api
paths:
  /api/v1/healthchecks:
    get:
      tags:
        - HealthChecks
      summary: get success response
      operationId: healthCheck
      responses:
        '200':
          description: return result ok
          content:
            application/json:
              schema:
                #下記2行をコメントアウト
                #required:
                # - result
                type: object
                properties:
                  result:
                    type: string
                    example: ok

先程同様、間違えたプロパティを返すようにコントローラーを使用します。

app/controllers/api/v1/healthchecks_controller.rb
module Api
  module V1
    class HealthchecksController < ApplicationController
      def index
        render json: { hoge: 'ok' }
        # render json: { result: 'ok' }
      end
    end
  end
end

この状態でテストを実行します。

rspec

すると、commiteeの方のテストケースは通ってしまいます。
このようにrequiredをつけていないとプロパティの検証が行われないので注意が必要です。

CircleCIの導入

テスト、リンターを実装したので、これらを自動実行するためのCIを導入します。
CIといってもいろいろな種類がありますが、本記事では、CircleCIを使用します。

前述した通り、詳細な説明はここではしませんが、CircleCIが何かよくわからない、
という方は下記の記事がとてもわかりやすかったので読んでみてください。

また、アカウントをお持ちでない方は下記から、アカウント登録してください。

.circle/config.ymlの作成

諸々の準備が終わったら、実装をスタートします。
まず、サイドバーのProjectsをクリックして、プロジェクト一覧ページに移動します。

当然ですが、github上に本アプリケーション用のrepositoryを作成していない場合、Projectsには表示されないので適宜repositoryを作成してください。
git操作やgithub上にrepositoryを作成する工程は本記事では扱いません。

dashboard.png

次に、CIを導入したい、projectのSet Up Projectsをクリックします
今回作成したのは、minimal_appなので、minimal_appのSet Up Projectsをクリックしました。
ci_projects.png

すると、config.ymlファイルを選択するためのモーダルが表示されます。

select _config_yml.png

表示されているオプションは

  1. すでにrepository上に存在している.circleci/config.ymlを使用する
  2. circleci側で用意しているcircleci/config.ymlを自動で作成された新しいbranchにcommitし、即CIの実行をする(CIの動作確認用)
  3. 編集可能なciのテンプレートを取得する

今回は、3番目のオプションを指定してみます。
すると、下記のようなEditorが開き、config.ymlのテンプレートが表示されます。

ci_editor.png

この中で、config.ymlの設定が行えます。
自分は下記のように設定しました。

.circle/config.yml
version: 2.1
jobs:
    test:
        docker:
            - image: cimg/ruby:3.1.3
              environment:
                BUNDLE_PATH: vendor/bundle
                RAILS_ENV: test
            - image: cimg/mysql:8.0
              name: db
              environment:
                  MYSQL_ROOT_PASSWORD: passw0rd
        steps:
            - checkout
            - restore_cache:
                keys:
                    - v1-dependencies-{{ checksum "Gemfile.lock" }}
                    - v1-dependencies-
            # set up
            - run:
                name: install gems
                command:
                    bundle install
            - save_cache:
                paths:
                  - ./vendor/bundle
                key: v1-dependencies-{{ checksum "Gemfile.lock" }}
            - run:
                name: wait to setup db
                command: dockerize -wait tcp://db:3306 -timeout 1m
            - run:
                name: setup db
                command: bundle exec rails db:create db:schema:load
            # run test
            - run:
                name: run test
                command: bundle exec rspec --profile 10
            # run linter
            - run:
                name: run linter
                command: bundle exec rubocop

workflows:
  sample:
    jobs:
      - test

ざっくり解説すると、大枠の流れは下記になっています。

  1. ruby, mysqlのイメージをビルド
  2. gemのインストール
  3. test用のdb準備(作成&スキーマ読み込み)
  4. rspecの実行
  5. rubocopの実行

その他で言えば、
2.のgemインストールの前後にrestore_cache, store_cacheを挟んでいます。
これは、依存関係(gem)のインストールを毎回実行するのではなく、変更された場合のみ新しい依存関係のインストールするようにするためです。
依存関係のキャッシュについては、公式ドキュメントに詳しく記載があるので、気になる方はご参照ください。

また、3.のtest用のdb準備の前に下記のコマンドを実行しています。

dockerize -wait tcp://db:3306 -timeout 1m

dockerizeはdockerでアプリケーションを実行するためのユーティリティツールで、
Dockerの公式ドキュメントでも紹介されています。

ここではtcp://db:3306つまり、mysqlイメージが正常にビルドされ、起動するまで待機するのに使用しています。
これがないと、dbのセットアップがmysqlサーバーの起動を追い越し、下記のエラーが発生することがあるので注意です。

Mysql2::Error::ConnectionError: Can't connect to MySQL server on 'db:3306'

CIの実行

config.ymlの設定が終わったら、Editor画面右上にある、Commit and Runを実行すると、
circleci-project-setupというブランチが作成され、config.ymlをそこにcommitし、
パイプラインが起動します。

スクリーンショット 2022-12-17 13.18.47.png

Successとなっていたら成功です。
問題なく実行できたら、circleci-project-setupをmainブランチにMergeしましょう。
ちなみに、git hub上でも、CIの結果が表示され、このマークをクリックするとCicleCIの画面に遷移することができます。

スクリーンショット 2022-12-17 13.20.35.png

何か不備があって変更がある場合は、git fetchなどを使用して、手元にcircleci-project-setupを持ってきて、
そこでconfig.ymlを編集し、commitしてpushを行うと、再度パイプラインが実行されます。

内容に不備がないのに、config.ymlのバリデーションで警告が出る場合は、
インデントがスペース4つ分になっているかどうか確認してください。

その他Railsの設定

CORSポリシーの設定

CORSポリシーとは

CORSとはオリジン間リソース共有(Cross-Origin Resource Sharing)のことで、
ざっくりいうと、他のオリジンから、アクセスを許可するしくみのことです。

オリジンとは、スキーム、ホスト、ポート番号を組み合わせて定義される値のことで、
https://localhost:80のように表記されるものです。

Web上では、悪意のあるサイトからのアクセスを防ぐため、
同一オリジン間のアクセスしか許可できないようにする仕組み(Same-Origin Policy)があります。

しかし、このままだとSwagger UIからリクエストを送信した時のように、自分がアクセスを許可したいサイトからのアクセスもブロックされてしまいます。

特定のサイトからのアクセスを許可するためには、
このオリジンのサイトにはこのリソースの共有を許可するよ!という設定をしてあげる必要があります。
それがCORSポリシーというわけです。

詳しくは下記をご参照ください。

CORSポリシーの実装

Railsではrack-corsというgemを使うことでCORSの設定をすることができます。
そしてこのライブラリはrails newした際にコメントアウトした状態で生成されているので、コメントを解除し、bundle installします。

Gemfil
...
gem "rack-cors"
...

そして、config/initializers/cors.rbに必要な設定を追記します。
ちなみにこのファイルもrails . newの時に生成されています。
本記事では、FEの実装をしていないため、
仮にhttp://localhost:3000 でFEアプリケーションが動作している場合の例で設定してみます。

config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    if Rails.env.development?
      origins 'localhost:3000'
    end

    resource '/api/v1/*',
      headers: :any, 
      methods: [:get, :post, :patch, :put, :delete]
  end
end

この設定で、http://localhost:3000 のオリジンを持つFEアプリケーションから、
api/v1以下のリソースにアクセスすることができるようになりました。
これで、CORSの設定は完了です。

originを複数指定したい場合は

origin 'localhost:3000'
origin 'localhost:4000'

ではなく、

origin 'localhost:3000', 'localhost:4000'

とします。
前者だと、localhost:4000の方しか反映されないので注意してください。

jbuilderの導入

jbuilderは、Railsで標準的に使用されているJSON用のテンプレートエンジンです。
minimalオプションをつけるとskipされてしまうので、個別にinstallする必要があります。

Gemfile
...
gem 'jbuilder'
...

GET /api/v1/healthchecksのendpointをjbuilderを使用する形に修正してみましょう。

appディレクトリの下にviews/api/v1/ディレクトリを作成し、そこに
下記のindex.json.jbuilderを配置します。
controllerから暗黙的にrenderするために、ディレクトリ名、ファイル名が重要なので、間違えないようにしてください。

app/views/api/v1/index.json.jbuilder
# frozen_string_literal: true

json.result @result

次に、healthchecks_controller.rbを次のように修正します。

app/controllers/api/v1/healthchecks_controller.rb
# frozen_string_literal: true

module Api
  module V1
    class HealthchecksController < ApplicationController
      def index
        @result = 'ok' #<= ここを変更
      end
    end
  end
end

rspecを実行して、テストが通れば成功です。

終わりに

これでAPIを実装する上で必要なものは最低限揃ったはずです。
本当は、この構成を使ってサンプルのCRUD APIを実装しようと思っていたのですが、
思いの外長くなってしまったのでまたの機会とさせていただきます。
最後まで読んでくださりありがとうございました。

参考

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?