はじめに
今回はRails5のAPIモードを利用してRest API(以下API)を作成する手順を説明します。
作成するAPIは1:多の関連を持つモデルに対してCRUD操作を行うAPIです。まずは親テーブル(1:多の1に該当するほう)に関するAPIを作成し、その後入れ子の形で子テーブル(1:多の多に該当するほう)を作成していきます。ソースコードの雛形はscaffoldで作成します。
また、APIの検証方法としてseeds.rb
にテストデータを用意し、curl
コマンドで確認を行うという方法をとります。
CRUD操作を行うAPIを作成後、追加機能として検索機能を作成します。
APIの概要
今回作成するAPIについてですが、先ほども説明をしたように、1:多の関連を持つモデルに対してCRUD操作を行うAPIです。
詳細は以下のようになっています。
ルーティング
以下が最終的に作成するAPIのルーティングになっています。
$ rails routes
Prefix Verb URI Pattern Controller#Action
search_events GET /events/search(.:format) events#search
event_comments GET /events/:event_id/comments(.:format) comments#index
POST /events/:event_id/comments(.:format) comments#create
event_comment GET /events/:event_id/comments/:id(.:format) comments#show
PATCH /events/:event_id/comments/:id(.:format) comments#update
PUT /events/:event_id/comments/:id(.:format) comments#update
DELETE /events/:event_id/comments/:id(.:format) comments#destroy
events GET /events(.:format) events#index
POST /events(.:format) events#create
event GET /events/:id(.:format) events#show
PATCH /events/:id(.:format) events#update
PUT /events/:id(.:format) events#update
DELETE /events/:id(.:format) events#destroy
ER図
以下が今回登場するテーブルのER図です。Eventが親テーブルにあたり、Commentが子テーブルにあたります。
親テーブルに対するAPIの作成
まずは、親テーブルにあたるEventモデルのCRUD APIを作成する手順について説明します。
APIの作成
APIモードでプロジェクトを作成します。
$ rails new railsAPI-OneToMany --api
scaffold
で親テーブルとなるEventモデルを作成します。
$ rails g scaffold Event title:string body:text
DBにテーブルを作成します。(Rails5からrakeコマンドがrailsに統合されています。)
$ rails db:migrate
DBに接続すると、events
テーブルが作成されているのがわかります。
$ rails dbconsole
SQLite version 3.14.0 2016-07-26 15:17:14
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata events schema_migrations
sqlite> .schema events
CREATE TABLE "events" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar, "body" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
テストデータの作成
seeds.rb
にテストデータを作成します。
Event.create({title: 'event1 title', body: 'this is the body of event1'})
Event.create({title: 'event2 title', body: 'this is the body of event2'})
seeds.rb
に作成したテストデータをDBに保存するには以下のコマンドで行います。
$ rails db:seed
APIの検証
rooutes
コマンドを確認すると、現時点では以下のようなルーティングになっているのがわかります。
$ rails routes
Prefix Verb URI Pattern Controller#Action
events GET /events(.:format) events#index
POST /events(.:format) events#create
event GET /events/:id(.:format) events#show
PATCH /events/:id(.:format) events#update
PUT /events/:id(.:format) events#update
DELETE /events/:id(.:format) events#destroy
サーバーを起動します。
$ rails s
http://localhost:3000/events にアクセスして、以下のような結果が表示されればOKです。
[
{
"id": 1,
"title": "event1 title",
"body": "this is the body of event1",
"created_at": "2017-01-01T07:09:22.348Z",
"updated_at": "2017-01-01T07:09:22.348Z"
},
{
"id": 2,
"title": "event2 title",
"body": "this is the body of event2",
"created_at": "2017-01-01T07:09:22.356Z",
"updated_at": "2017-01-01T07:09:22.356Z"
}
]
http://localhost:3000/events/1 にアクセスしてid
が1
のEventのデータだけ返されればOKです。
{
"id": 1,
"title": "event1 title",
"body": "this is the body of event1",
"created_at": "2017-01-01T07:09:22.348Z",
"updated_at": "2017-01-01T07:09:22.348Z"
}
子テーブルに対するAPIの作成
次に、子テーブルにあたるCommentモデルのCRUD APIを作成する手順について説明します。
APIの作成
まずは先ほどと同様にscaffold
で子要素となるCommentモデルを作成します
$ rails g scaffold Comment event:references body:text
DBにテーブルを作成します。
$ rails db:migrate
1:多の関係にするために親テーブルにあたるEventモデルを以下のように変更します。
これで、一つのEventに対して複数のCommentが紐づくようになります。
class Event < ApplicationRecord
+ has_many :comments
end
親子関係のテーブルに対するAPIなので、ルーティングも入れ子になるようにroutes.rbを以下のように変更します。
Rails.application.routes.draw do
- resources :comments
- resources :events
+ resources :events do
+ resources :comments
+ end
end
Commentモデルのcomments_controller.rb
の編集をしていきます。
変更が必要になるメソッドはindex
、create
、set_user
です。
locationを削除する理由としては、削除しないとNoMethodError: undefined method comment_url
というエラーが出るためです。
def create
- @comment = Comment.new(comment_params)
+ @event = Event.where(:id => params[:event_id]).first
+ @comment = @event.comments.build(comment_params)
if @comment.save
- render json: @comment, status: :created, location: @comment
+ render json: @comment, status: :created
def index
- @comments = Comment.all
+ @event = Event.where(:id => params[:event_id]).first
+ @comments = @event.comments.all
render json: @comments
end
def set_comment
- @comment = Comment.find(params[:id])
+ @event = Event.where(:id => params[:event_id]).first
+ @comment = @event.comments.where(:id => params[:id]).first
end
テストデータの作成
seeds.rb
にテストデータを追加します。
上半分はid
が1
の、下半分はid
が2
のEventに紐づくCommentのテストデータです。
Comment.create({body: 'comment1 body of Event1', event_id: 1})
Comment.create({body: 'comment2 body of Event1', event_id: 1})
Comment.create({body: 'comment3 body of Event2', event_id: 2})
Comment.create({body: 'comment4 body of Event2', event_id: 2})
rails db:seed
を複数回実行することによって同じテストデータが入らないようにするためは、以下のように実行します。
はじめのコマンドで既存のテーブルに存在するデータをまっさらな状態になり、そのあとのコマンドでテストデータが作成されます。
$ rails db:migrate:reset
$ rails db:seed
APIの検証
rooutes
コマンドを確認すると、以下のようなルーティングになっているのがわかります。
$ rails routes
Prefix Verb URI Pattern Controller#Action
event_comments GET /events/:event_id/comments(.:format) comments#index
POST /events/:event_id/comments(.:format) comments#create
event_comment GET /events/:event_id/comments/:id(.:format) comments#show
PATCH /events/:event_id/comments/:id(.:format) comments#update
PUT /events/:event_id/comments/:id(.:format) comments#update
DELETE /events/:event_id/comments/:id(.:format) comments#destroy
events GET /events(.:format) events#index
POST /events(.:format) events#create
event GET /events/:id(.:format) events#show
PATCH /events/:id(.:format) events#update
PUT /events/:id(.:format) events#update
DELETE /events/:id(.:format) events#destroy
サーバーを起動します。
$ rails s
Getメソッドの検証
以下のようにして、id
が1
のEventに紐づくCommentを取得できればOKです。
$ curl http://localhost:3000/events/1/comments/
[{"id":1,"event_id":1,"body":"comment1 body of Event1","created_at":"2017-01-01T07:09:22.392Z","updated_at":"2017-01-01T07:09:22.392Z"},{"id":2,"event_id":1,"body":"comment2 body of Event1","created_at":"2017-01-01T07:09:22.401Z","updated_at":"2017-01-01T07:09:22.401Z"}]%
POSTメソッドの検証
以下のようにして、新しいEventを作成できればOKです。
$ curl -v -H "Accept: application/json" -H "Content-type: application/json" POST -d '{"title":"event3 title", "body":"this is the body of event3"}' http://localhost:3000/events
以下のようにして、id
が1
のEventに紐づく新しいCommentを作成できればOKです。
$ curl -v -H "Accept: application/json" -H "Content-type: application/json" POST -d '{"body":"comment3 body of Event1"}' http://localhost:3000/events/1/comments
DELETEメソッドの検証
以下のようにして、id
が3
のEventを削除できればOKです。
$ curl -X DELETE http://localhost:3000/events/3
以下のようにして、id
が1
のEventに紐づくid
が5
のCommnetを削除できればOKです。
$ curl -X DELETE http://localhost:3000/events/1/comments/5
PUTメソッドの検証
以下のようにして、id
が2
のEventを編集できればOKです。
$ curl -X PUT -H 'Content-Type:application/json' -d '{ "event" : { "title": "Event2 title" } }' http://localhost:3000/events/2
検索機能の追加
上記の方法でCRUD操作を行うAPIを作成できました。
ここでは追加機能として、Eventをtitle名で検索するAPIを作成します。
まずはコントローラーに検索用のメソッドを作成します。
def search
@events = Event.find_by_title(params[:title])
render json: @events
end
routes.rbを以下のように変えます。これで一番はじめに掲載したルーティングになります。
Rails.application.routes.draw do
resources :events do
+ collection do
+ get :search
+ end
resources :comments
end
end
APIの検証
URIを直接叩いて検証する場合は以下のようになります。
event1 title
というタイトルのEventが返ってくればOKです。
http://localhost:3000/events/search?title=event1 title
にアクセスして、以下のような結果が表示されればOKです。
{
"id": 1,
"title": "event1 title",
"body": "this is the body of event1",
"created_at": "2017-01-01T07:09:22.348Z",
"updated_at": "2017-01-01T07:09:22.348Z"
}
curlなら以下のようにして確認します。
$ curl -X GET -d "title=event1 title" http://localhost:3000/events/search
{"id":1,"title":"event1 title","body":"this is the body of event1","created_at":"2017-01-01T07:09:22.348Z","updated_at":"2017-01-01T07:09:22.348Z"}%