RailsでWEB API入門

# はじめに
今回はRESTfulなAPI開発の基礎的なところを勉強していきます．
なお，本記事は[Build a RESTful JSON API With Rails 5 - Part One](https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-one)の内容を自分のために翻訳＋自分にとって分かりやすくまとめたものになります．

最終的なコードは[Github](https://github.com/yyh-gl/rails-web-api)にあげてあります．


# 開発環境
- Mac
- Ruby 2.5.0
- Rails 5.1.4


# エンドポイント
今回作成するAPIのエンドポイントは以下のようになっています．

|エンドポイント|機能|
|:--:|:--:|
|POST /signup|Signup|
|POST /auth/login|Login|
|GET /auth/logout|Logout|
|GET /todos|List all todos|
|POST /todos|Create a new todo|
|GET /todos/:id|Get a todo|
|PUT /todos/:id|Update a todo|
|DELETE /todos/:id|Delete a todo and its items|
|GET /todos/:id/items|Get a todo item|
|PUT /todos/:id/items|Update a todo item|
|DELETE /todos/:id/items|Delete a todo item|


# セットアップ
## プロジェクト作成

```zsh:
$ rails new todos-api --api -T
```

- todos-api：プロジェクト名
- --api：API開発モードの指定オプション
- -T：デフォルトのテスティングフレームワーク「Minitest」をオフにするオプション

今回はテスティングフレームワークとして[RSpec](http://rspec.info/)を使用．

*- ポイント -*
「--api」オプションによりAPI開発を行うことをRailsに知らせる．


## Gem インストール
以下のGemを利用します．

- spec-rails：テスティングフレームワーク
- faker：テストデータの作成
- factory_girl_rails：テストデータの作成補助
- shoulda_matchers：テストのコーディング補助
- database_cleaner：データベースの掃除屋さん

Gemfileに追記

```ruby:Gemfile
# devとtest環境でのみ使用
group :development, :test do
  gem 'rspec-rails', '~> 3.5'
end

# test環境でのみ使用
group :test do
  gem 'factory_girl_rails', '~> 4.0'
  gem 'shoulda-matchers', '~> 3.1'
  gem 'faker'
  gem 'database_cleaner'
end
```
gem インストール

```zsh:
$ bundle install
```

## RSpecの利用
RSpecの初期化

```zsh:
$ rails generate rspec:install
```

Factory Girl用のディレクトリ作成

```zsh:
$ mkdir spec/factories
```

最後に，RSpecの設定ファイルを編集します．

```ruby:spec/rails_helper.rb
# require database cleaner at the top level
require 'database_cleaner'

# [...]
# configure shoulda matchers to use rspec as the test framework and full matcher libraries for rails
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

# [...]
RSpec.configure do |config|
  # [...]
  # add `FactoryGirl` methods
  config.include FactoryGirl::Syntax::Methods

  # start by truncating all the tables but then use the faster transaction strategy the rest of the time.
  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
    DatabaseCleaner.strategy = :transaction
  end

  # start the transaction strategy as examples are run
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end
  # [...]
end
```


とりあえず，ここまででRailsでAPIを開発していくためのセットアップは完了．

次章からどんどん中身を作っていきます．


# モデルの作成
## Todoモデル
まずはTodoモデルを作っていきます．

```zsh:
$ rails g model Todo title:string created_by:string
```

中身はこんな感じ（勝手にできてます）

```ruby:db/migrate/[timestamp]_create_todos.rb
class CreateTodos < ActiveRecord::Migration[5.0]
  def change
    create_table :todos do |t|
      t.string :title
      t.string :created_by

      t.timestamps
    end
  end
end
```

上記コードより
titleおよびcreated_byというカラムを持ったtodosテーブルが作成されます．

## Itemモデル
次はItemモデルの作成

```zsh:
$ rails g model Item name:string done:boolean todo:references
```

中身はこんな感じ（勝手にできてます）

```ruby:db/migrate/[timestamp]_create_items.rb
class CreateItems < ActiveRecord::Migration[5.0]
  def change
    create_table :items do |t|
      t.string :name
      t.boolean :done
      t.references :todo, foreign_key: true

      t.timestamps
    end
  end
end
```

上記コードより
nameおよびdoneというカラムを持ったitemsテーブルができます（4, 5行目）．

なお，itemsテーブルでは，todoカラム（todo_id）が外部キーに指定されています（6行目）．


一通りモデルを書き終わったので，ここでmigrate

```zsh:
$ rails db:migrate
```


## テストコードの作成
本記事では，「赤→緑→リファクタリング」の手順で開発を進めます．
よって，RSpecを用いて，まずはテストコードを作っていきます．
（リファクタリングはしてないですが・・・）

### Todoモデルに対するテスト
Todoモデルに対しては以下のテストを行います．

- TodoモデルがItemモデルと1対mの関係である（下記コード7行目）
- 保存前にtitleカラムが存在する（下記コード10行目）
- 保存前にcreated_byカラムが存在する（下記コード11行目）

```ruby:spec/models/todo_spec.rb
require 'rails_helper'

# Test suite for the Todo model
RSpec.describe Todo, type: :model do
  # Association test
  # ensure Todo model has a 1:m relationship with the Item model
  it { should have_many(:items).dependent(:destroy) }
  # Validation tests
  # ensure columns title and created_by are present before saving
  it { should validate_presence_of(:title) }
  it { should validate_presence_of(:created_by) }
end
```

### Itemモデルに対するテスト
Itemモデルに対しては以下のテストを行います．

- itemレコードはただひとつのtodoレコードに属する（下記コード7行目）
- 保存前にnameカラムが存在する（下記コード10行目）

```ruby:spec/models/item_spec.rb
require 'rails_helper'

# Test suite for the Item model
RSpec.describe Item, type: :model do
  # Association test
  # ensure an item record belongs to a single todo record
  it { should belong_to(:todo) }
  # Validation test
  # ensure column name is present before saving
  it { should validate_presence_of(:name) }
end
```

### テストの実行
テストを実行します．

```zsh:
$ bundle exec rspec
```
するとひとつのテストをパスしますが，以下の4つのエラーが出ると思います．

- Item should validate that :name cannot be empty/falsy
- Todo should have many items dependent => destroy
- Todo should validate that :title cannot be empty/falsy
- Todo should validate that :created_by cannot be empty/falsy

エラーの内3つは中身がないよというエラーで，後の1つは依存関係のエラーです．

そりゃ，モデルのマイグレーションファイルを作っただけで，中身をちゃんと作ってないんですからエラーを吐かれて当然ですね．

次はこれらの赤色達を緑色にしていきます．

## モデルの修正
モデルの中身をしっかり作っていきましょう．

```ruby:app/models/todo.rb
class Todo < ApplicationRecord
  # model association
  has_many :items, dependent: :destroy

  # validations
  validates_presence_of :title, :created_by
end
```

3行目の「has_many」は，Todoモデルが複数のitemsを持つことを意味します．
同じく3行目の「dependent」は，親であるTodoが削除されたときに子であるitemsをどうするかを決定する．今回はTodoが消えれば，それに付随するitemsも削除することにする．
6行目の「validates_presence_of」は，指定したカラムにレコードが存在するかを確認するバリデーションである．

```ruby:app/models/item.rb
class Item < ApplicationRecord
  # model association
  belongs_to :todo

  # validation
  validates_presence_of :name
end
```

3行目の「belongs_to」は，先程のtodo.rbの「has_many」に対応しており，Itemがtodoに追従することを示す．

以上でモデルの中身ができたのでテストを実行します．
すると，5個全てのテストが緑色に変わったと思います．

さて，これでモデルが完成したので，次からはコントローラの作成に移ります．


# コントローラの作成
コントローラにより，どのURIを叩けばどういったデータが得られるか決まってきます．

とりあえずRailsさんの力を借りてコントローラの土台を作ってしまいます．


```zsh:
$ rails g controller Todos && rails g controller Items
```

## Todosコントローラ用のテストコード作成
モデルと同様にコントローラ用のテストを作ります．
まずは，APIを叩いたときの挙動に関するテストです．

では，初めにTodoコントローラに対してテストを作っていきます．

APIリクエスト関連のテストコードを格納するディレクトリおよび対応ファイルを作成．

```zsh:
$ mkdir spec/requests && touch spec/requests/todos_spec.rb 
```

### Todosコントローラに対するテスト
Todosコントローラに対しては以下のテストを行います．

- 「GET /todos」を叩いたときレコードが空でなく，10個存在することを確認する（下記コード13行目）
- 「GET /todos」を叩いたときリクエストが正常であれば，ステータスコード200を返す（下記コード19行目）
- 「GET /todos/:id」を叩いたとき該当IDのレコードが存在すれば，そのレコードが空でなくidがtodo_idと同じか確認する（下記コード29行目）
- 「GET /todos/:id」を叩いたときリクエストが正常であれば，ステータスコード200を返す（下記コード34行目）
- 「GET /todos/:id」を叩いたときに該当レコードがなければ，エラーメッセージを返す（下記コード46行目）
- 「GET /todos/:id」を叩いたときに該当レコードがなければ，ステータスコード404を返す（下記コード42行目）
- 「POST /todos」を叩いたときリクエストが正常であれば，「Learn Elm」というtodoを作成できているか確認する（下記コード60行目）
- 「POST /todos」を叩いたときリクエストが正常であれば，ステータスコード201を返す（下記コード64行目）
- 「POST /todos」を叩いたときリクエストが異常であれば，エラーメッセージを返す（下記コード76行目）
- 「POST /todos」を叩いたときリクエストが異常であれば，ステータスコード422を返す（下記コード72行目）
- 「PUT /todos/:id」を叩いたとき該当IDのレコードが存在すれば，それが空であることを確認する（下記コード90行目）
- 「PUT /todos/:id」を叩いたときリクエストが正常であれば，ステータスコード204を返す（下記コード94行目）
- 「DELETE /todos/:id」を叩いたときリクエストが正常であれば，ステータスコード204を返す（下記コード104行目）

```ruby:spec/requests/todos_spec.rb
require 'rails_helper'

RSpec.describe 'Todos API', type: :request do
  # initialize test data 
  let!(:todos) { create_list(:todo, 10) }
  let(:todo_id) { todos.first.id }

  # Test suite for GET /todos
  describe 'GET /todos' do
    # make HTTP get request before each example
    before { get '/todos' }

    it 'returns todos' do
      # Note `json` is a custom helper to parse JSON responses
      expect(json).not_to be_empty
      expect(json.size).to eq(10)
    end

    it 'returns status code 200' do
      expect(response).to have_http_status(200)
    end
  end

  # Test suite for GET /todos/:id
  describe 'GET /todos/:id' do
    before { get "/todos/#{todo_id}" }

    context 'when the record exists' do
      it 'returns the todo' do
        expect(json).not_to be_empty
        expect(json['id']).to eq(todo_id)
      end

      it 'returns status code 200' do
        expect(response).to have_http_status(200)
      end
    end

    context 'when the record does not exist' do
      let(:todo_id) { 100 }

      it 'returns status code 404' do
        expect(response).to have_http_status(404)
      end

      it 'returns a not found message' do
        expect(response.body).to match(/Couldn't find Todo/)
      end
    end
  end

  # Test suite for POST /todos
  describe 'POST /todos' do
    # valid payload
    let(:valid_attributes) { { title: 'Learn Elm', created_by: '1' } }

    context 'when the request is valid' do
      before { post '/todos', params: valid_attributes }

      it 'creates a todo' do
        expect(json['title']).to eq('Learn Elm')
      end

      it 'returns status code 201' do
        expect(response).to have_http_status(201)
      end
    end

    context 'when the request is invalid' do
      before { post '/todos', params: { title: 'Foobar' } }

      it 'returns status code 422' do
        expect(response).to have_http_status(422)
      end

      it 'returns a validation failure message' do
        expect(response.body)
          .to match(/Validation failed: Created by can't be blank/)
      end
    end
  end

  # Test suite for PUT /todos/:id
  describe 'PUT /todos/:id' do
    let(:valid_attributes) { { title: 'Shopping' } }

    context 'when the record exists' do
      before { put "/todos/#{todo_id}", params: valid_attributes }

      it 'updates the record' do
        expect(response.body).to be_empty
      end

      it 'returns status code 204' do
        expect(response).to have_http_status(204)
      end
    end
  end

  # Test suite for DELETE /todos/:id
  describe 'DELETE /todos/:id' do
    before { delete "/todos/#{todo_id}" }

    it 'returns status code 204' do
      expect(response).to have_http_status(204)
    end
  end
end
```

次にTodoコントローラ用のテストデータを作成します．

```zsh:
$ touch spec/factories/todos.rb
```

```ruby:spec/factories/todos.rb
FactoryGirl.define do
  factory :todo do
    title { Faker::Lorem.word }
    created_by { Faker::Number.number(10) }
  end
end
```

加えて，今回のテストをサポートするものを作っていきます．


```zsh:
$ mkdir spec/support && touch spec/support/request_spec_helper.rb
```

下記のRequestSpecHelperは，JSONファイルで返ってくるものをRubyのハッシュに変換することで，テストしやすくします．

```ruby:spec/support/request_spec_helper.rb
module RequestSpecHelper
  # Parse JSON response to ruby hash
  def json
    JSON.parse(response.body)
  end
end
```

そして，作った上記のヘルパーをRSpecで使うことを宣言します．

```ruby:spec/rails_helper.rb
# [...]
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# [...]
RSpec.configure do |config|
  # [...]
  config.include RequestSpecHelper, type: :request
  # [...]
end
```

さて，ここまででテストする準備が整ったのでテストを実行してみます．

すると，先程Todoコントローラに対して追加した13個のテスト全てで”しっかりと”エラーが起きました．

さぁ，修正を加えて，テストを緑色にしていきましょう．

## Todosコントローラの修正
先程の13個のテストがエラーとなった原因は，ルート設定およびTodosコントローラの中身を実装していないからなので，そこら辺の実装をやっていきます．

まずは，ルートを設定していきます．

```ruby:config/routes.rb
Rails.application.routes.draw do
  resources :todos do
    resources :items
  end
end
```

2, 3行目の「resources」でコントローラを指定することで簡単にルーティングが設定できました．なお，itemsはネスト形式で書くことがポイントです．こうすることでtodosコントローラを基に定義したルーティングに属する形でitemsコントローラのルーティングを設定できます．

次に，Todosコントローラの中身です．

```ruby:app/controllers/todos_controller.rb
class TodosController < ApplicationController
  before_action :set_todo, only: [:show, :update, :destroy]

  # GET /todos
  def index
    @todos = Todo.all
    json_response(@todos)
  end

  # POST /todos
  def create
    @todo = Todo.create!(todo_params)
    json_response(@todo, :created)
  end

  # GET /todos/:id
  def show
    json_response(@todo)
  end

  # PUT /todos/:id
  def update
    @todo.update(todo_params)
    head :no_content
  end

  # DELETE /todos/:id
  def destroy
    @todo.destroy
    head :no_content
  end

  private

  def todo_params
    # whitelist params
    params.permit(:title, :created_by)
  end

  def set_todo
    @todo = Todo.find(params[:id])
  end
end
```

上記コードでは，「POST /todos」や「GET /todos/:id」に対応したメソッドを定義しています．中身はデータベースのCRUD操作です．

2行目にある「before_action :set_todo, only: [:show, :update, :destroy]」は，アクション実行前に「set_todo」メソッドを実行することを示します．ただし，「only: [:show, :update, :destroy]」によって，show，update，destroyメソッド実行前にのみ「set_todo」を実行するように制限しています．


24, 30行目にある「head :no_content」は，返却するステータスコードを指定しています．「no_content」は204を返します．

さらに，レスポンスをJSONで返すために次のコードを加えます．

```app/controllers/concerns/response.rb
module Response
  def json_response(object, status = :ok)
    render json: object, status: status
  end
end
```

さらにさらに，レコードが無いまたは不正な場合にエラーメッセージとステータスコード404を返すために以下のハンドラを追加します．

```app/controllers/concerns/exception_handler.rb
module ExceptionHandler
  # provides the more graceful `included` method
  extend ActiveSupport::Concern

  included do
    rescue_from ActiveRecord::RecordNotFound do |e|
      json_response({ message: e.message }, :not_found)
    end

    rescue_from ActiveRecord::RecordInvalid do |e|
      json_response({ message: e.message }, :unprocessable_entity)
    end
  end
end
```

最後に作成したヘルパーをコントローラでincludeします．

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include Response
  include ExceptionHandler
end
```

さぁ，今こそテストを実行するときです！
全て緑色になってますね：）


## Itemsコントローラ用のテストコード作成
Todosコントローラと同様にItemsコントローラもテスト→コントローラ内部の順で作っていきます．
初めにspecファイルを作成します．

```zsh:
$ touch spec/requests/items_spec.rb 
```

### Todosコントローラに対するテスト
Itemsコントローラに対しては以下のテストを行います．

- 「GET /todos/:todo_id/items」を叩いたときTODOが存在すれば，全て（20個）のTODOアイテムを返す（下記コード19行目）
- 「GET /todos/:todo_id/items」を叩いたときリクエストが正常であればステータスコード200を返す（下記コード15行目）
- 「GET /todos/:todo_id/items」を叩いたときTODOが存在しなければ，エラーメッセージを返す（下記コード31行目）
- 「GET /todos/:todo_id/items」を叩いたときTODOが存在しなければ，ステータスコード404を返す（下記コード27行目）
- 「GET /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在すれば，該当するTODOアイテムを返す（下記コード46行目）
- 「GET /todos/:todo_id/items/:id」を叩いたときリクエストが正常であれば，ステータスコード200を返す（下記コード42行目）
- 「GET /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在しなければ，エラーメッセージを返す（下記コード58行目）
- 「GET /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在しなければ，ステータスコード404を返す（下記コード54行目）
- 「POST /todos/:todo_id/items」を叩いたときリクエストが正常であれば，ステータスコード201を返す（下記コード71行目）
- 「POST /todos/:todo_id/items」を叩いたときリクエストが異常であれば，エラーメッセージを返す（下記コード83行目）
- 「POST /todos/:todo_id/items」を叩いたときリクエストが異常であれば，ステータスコード422を返す（下記コード79行目）
- 「PUT /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在すれば，該当するTODOアイテムのnameが「Mozart」と一致するか確認する（下記コード100行目）
- 「PUT /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在すれば，ステータスコード204を返す（下記コード96行目）
- 「PUT /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在しなければ，エラーメッセージを返す（下記コード113行目）
- 「PUT /todos/:todo_id/items/:id」を叩いたときTODOアイテムが存在しなければ，ステータスコード404を返す（下記コード109行目）
- 「DELETE /todos/:id」を叩いたときリクエストが正常であれば，ステータスコード204を返す（下記コード123行目）

```ruby:spec/requests/items_spec.rb
require 'rails_helper'

RSpec.describe 'Items API' do
  # Initialize the test data
  let!(:todo) { create(:todo) }
  let!(:items) { create_list(:item, 20, todo_id: todo.id) }
  let(:todo_id) { todo.id }
  let(:id) { items.first.id }

  # Test suite for GET /todos/:todo_id/items
  describe 'GET /todos/:todo_id/items' do
    before { get "/todos/#{todo_id}/items" }

    context 'when todo exists' do
      it 'returns status code 200' do
        expect(response).to have_http_status(200)
      end

      it 'returns all todo items' do
        expect(json.size).to eq(20)
      end
    end

    context 'when todo does not exist' do
      let(:todo_id) { 0 }

      it 'returns status code 404' do
        expect(response).to have_http_status(404)
      end

      it 'returns a not found message' do
        expect(response.body).to match(/Couldn't find Todo/)
      end
    end
  end

  # Test suite for GET /todos/:todo_id/items/:id
  describe 'GET /todos/:todo_id/items/:id' do
    before { get "/todos/#{todo_id}/items/#{id}" }

    context 'when todo item exists' do
      it 'returns status code 200' do
        expect(response).to have_http_status(200)
      end

      it 'returns the item' do
        expect(json['id']).to eq(id)
      end
    end

    context 'when todo item does not exist' do
      let(:id) { 0 }

      it 'returns status code 404' do
        expect(response).to have_http_status(404)
      end

      it 'returns a not found message' do
        expect(response.body).to match(/Couldn't find Item/)
      end
    end
  end

  # Test suite for PUT /todos/:todo_id/items
  describe 'POST /todos/:todo_id/items' do
    let(:valid_attributes) { { name: 'Visit Narnia', done: false } }

    context 'when request attributes are valid' do
      before { post "/todos/#{todo_id}/items", params: valid_attributes }

      it 'returns status code 201' do
        expect(response).to have_http_status(201)
      end
    end

    context 'when an invalid request' do
      before { post "/todos/#{todo_id}/items", params: {} }

      it 'returns status code 422' do
        expect(response).to have_http_status(422)
      end

      it 'returns a failure message' do
        expect(response.body).to match(/Validation failed: Name can't be blank/)
      end
    end
  end

  # Test suite for PUT /todos/:todo_id/items/:id
  describe 'PUT /todos/:todo_id/items/:id' do
    let(:valid_attributes) { { name: 'Mozart' } }

    before { put "/todos/#{todo_id}/items/#{id}", params: valid_attributes }

    context 'when item exists' do
      it 'returns status code 204' do
        expect(response).to have_http_status(204)
      end

      it 'updates the item' do
        updated_item = Item.find(id)
        expect(updated_item.name).to match(/Mozart/)
      end
    end

    context 'when the item does not exist' do
      let(:id) { 0 }

      it 'returns status code 404' do
        expect(response).to have_http_status(404)
      end

      it 'returns a not found message' do
        expect(response.body).to match(/Couldn't find Item/)
      end
    end
  end

  # Test suite for DELETE /todos/:id
  describe 'DELETE /todos/:id' do
    before { delete "/todos/#{todo_id}/items/#{id}" }

    it 'returns status code 204' do
      expect(response).to have_http_status(204)
    end
  end
end
```

テストデータも忘れずに作りましょう．

```ruby:spec/factories/items.rb
FactoryGirl.define do
  factory :item do
    name { Faker::StarWars.character }
    done false
    todo_id nil
  end
end
```

では，テストを実行してみます．

結果は，先程のTodoコントローラのときと同じようにエラーが出ます（16個）．

修正を加えて，テストを緑色にしていきましょう．

## Itemsコントローラの修正
Itemsコントローラの中身を作っていきます．

```ruby:app/controllers/items_controller.rb
class ItemsController < ApplicationController
  before_action :set_todo
  before_action :set_todo_item, only: [:show, :update, :destroy]

  # GET /todos/:todo_id/items
  def index
    json_response(@todo.items)
  end

  # GET /todos/:todo_id/items/:id
  def show
    json_response(@item)
  end

  # POST /todos/:todo_id/items
  def create
    @todo.items.create!(item_params)
    json_response(@todo, :created)
  end

  # PUT /todos/:todo_id/items/:id
  def update
    @item.update(item_params)
    head :no_content
  end

  # DELETE /todos/:todo_id/items/:id
  def destroy
    @item.destroy
    head :no_content
  end

  private

  def item_params
    params.permit(:name, :done)
  end

  def set_todo
    @todo = Todo.find(params[:todo_id])
  end

  def set_todo_item
    @item = @todo.items.find_by!(id: params[:id]) if @todo
  end
end
```

上記コードでは，Todosコントローラと同様にItemコントローラ用に対応したメソッドを定義しています．中身はtodos_controller.rbとほぼ同じです．

これでテストを実行すると全て緑色になってますよね：）

# 実食！
以下のAPIを実際に叩いてみます．
利用するツールはcurlや[POSTMAN](https://www.getpostman.com/)，[HTTPie](https://httpie.org/)などなど，なんでもいいです．今回は参考サイトに合わせてHTTPieを使います．

まずはサーバを起動

```zsh:
$ rails s
```

下記に示すコマンドを実行して，APIを叩きに行きます．

```zsh:
# GET /todos
$ http :3000/todos

# POST /todos
$ http POST :3000/todos title=Mozart created_by=1

# PUT /todos/:id
$ http PUT :3000/todos/1 title=Beethoven

# DELETE /todos/:id
$ http DELETE :3000/todos/1

# GET /todos/:todo_id/items
$ http :3000/todos/2/items

# POST /todos/:todo_id/items
$ http POST :3000/todos/2/items name='Listen to 5th Symphony' done=false

# PUT /todos/:todo_id/items/:id
$ http PUT :3000/todos/2/items/1 done=true

# DELETE /todos/:todo_id/items/1
$ http DELETE :3000/todos/2/items/1
```

しっかりとレスポンスがきているかは，各自確認してみてください．

これでWEB APIの一通りの開発は終わりです！
おつかれさまでした．


# おわりに
RailsでAPIを作成する方法は，今回のRails5の--apiオプション以外にも，[rails-api](https://github.com/rails-api/rails-api)や[grape](https://github.com/ruby-grape/grape)といったGemを利用する方法もあるみたいです．
今後そちらも勉強してみたいと思います．
もしそれらの違いなどについて詳しい方がいればコメントなどで教えていただけると喜びます：）

本記事はほとんど（全て）自分の勉強用のメモですが，だれかの役に立てることを願っています．
ミスなどあればご指摘お願いします．

【注意】
今回参考にした資料では，テスト用データ作成にFactry GirlというGemを利用しました．
しかし，現在，Factry GirlはFactry BotというGem名に変わっています（[参考](https://github.com/thoughtbot/factory_bot/blob/master/NAME.md)）．
したがって，[こちら](https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md)を参考にFactry Botへの移行をおすすめします．


# 参考
[Build a RESTful JSON API With Rails 5 - Part One](https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-one)


# 追記（2018/11/18）：
[個人ブログ](https://yyh-gl.hatenablog.com)始めました。
よかったらこちらもどうぞ！