Rails APIでREST設計しようとすると、ControllerとURIをうまくネストされない！


APIをREST設計で作成する中でURIをネストさせますが、
そのまま作成していると
Controllerフォルダがすごいことに....

Controllerフォルダも階層分けして見やすくするか...
と考え実装すると

- URIはネストできたが、Controllerのファイルがネストされない？
- Controllerのファイルはネストしたが、URIが変わらない？
- URIだけ名前を変えたいんだけど...

となり、カオスな状態に。


...一度整理しましょう。

<br>

## まずREST設計とは？

APIを作成するなら、デファクトになりつつあるREST設計

ざっくりルール化するとすれば

- なるべくシンプルに
- 全てのリソースがURIで示せる
- URIは基本名詞で（動詞やメソッド名は❌）
- GET、POST、PUT、PATCH、DELETEを基本とする。
- バージョニング情報(v1等)をURIに含める

あたりでしょうか。

カスタムメソッドは認めないとか
HATEOASの実装とかは触れないことにします。

参考になったのはこの辺り

- [0からREST APIについて調べてみた - Qiita](https://qiita.com/masato44gm/items/dffb8281536ad321fb08)

- [RESTfulとは](https://www.slideshare.net/unsolublesugar/res-tful?next_slideshow=1)

- [RESTfulとは RailsスタイルからRESTを学ぼう](https://www.slideshare.net/tkawa1/learning-rest-from-rails-style)

<br>

## 実例:こんなURIはRESTfulとは認めない！

railsの場合は、従ってだけでRESTfulな設計になります。
例えば、

routeファイルでresourceをするだけで


```Ruby:config/route.rb
resources :users
```

６つのアクションが自動生成されますね。
これはREST設計になっています。

```
GET /users            users#index
POST /users           users#create
GET /users/:id        users#show
PATCH /users/:id      users#update
PUT /users/:id        users#update
DELETE /users/:id     users#destroy
```

さてここで、もう少し深掘りしてみましょう。

仮に

- usersテーブル
- usersと多対多の関係にあるclubsテーブル
- その中間テーブルであるusers_clubsテーブル

があるとします。

id=2のuserが所属しているclub
を表すURIは

```
/users/users_clubs/2　
```

となりますね....
<br>
<br>
$\huge{.....アウト！}$

<br>

イケてないURIですね。
修正版はこちら

```
/users/users_clubs/2　❌
/v1/users/2/clubs/　　　✅
```

イケてない点としては

- user_idを参照しているなら、users/:user_id/clubs とするべき
- users_clubの前にusersがあるため、userがもつclub情報なら、users_culbsとするのは冗長（clubsで良い）
- 修正前のURIのみを見た場合、club_idと勘違いするリスクがある
- バージョニング（V1）が含まれていない

あたりでしょうか。
それでは１つ１つ直していきましょう。

<br>
### namespaceを使用して、バージョニング情報の付与

Controller配下にV1フォルダを作成し
app/controllers/V1/user_controller.rb
となるように配置しましょう。

このようにすることで、今後APIの改修が入った際に
v2としてディレクトリも分けることが可能ですし、
URIも分けることができます。

コントローラーの配置はこのような形になります。

```
controller
 ┗ V1
    ┗ user.controller
```


route.rb側では
namespaceを使用することで、
URIもコントローラもネストすることができます

```Ruby:config/route.rb
namespace :V1 do
  resources :users
end
```

`rake routes` で確認してもURIに
綺麗にV1が入っていて、無事バージョニング情報を入れることができました。

```
# rake routes                    
GET    /V1/users(.:format)           
POST   /V1/users(.:format)           
GET    /V1/users/:id(.:format)       
PATCH  /V1/users/:id(.:format)       
PUT    /V1/users/:id(.:format)       
DELETE /V1/users/:id(.:format)       
```

バージョニング関係は、色々なやり方があります。

[#350 REST API Versioning](
http://railscasts.com/episodes/350-rest-api-versioning?view=asciicast)

gemなら[versionist](https://github.com/bploetz/versionist
)

https://stackoverflow.com/questions/9627546/api-versioning-for-rails-routes/9627796#9627796

などなど...


<br>
<br>

さて、user_culbsも追加していきましょう

## memberを使い、idを伴うURIを追加する


user:idを引き継いでuser_clubsを呼び出したいので
memberを使います


```Ruby:config/route.rb
namespace :V1, format: 'json' do
    resources :users do
　　　　member do
        get :user_clubs
        post :user_clubs
　　　　end
    end
 end
```

これで
V1/users/:id/user_clubsと
user_idを渡す形で作成できました。

```
            Prefix Verb   URI Pattern                        Controller#Action
user_clubs_V1_user GET    /V1/users/:id/user_clubs(.:format) V1/users#user_clubs {:format=>/json/}
                   POST   /V1/users/:id/user_clubs(.:format) V1/users#user_clubs {:format=>/json/}
          V1_users GET    /V1/users(.:format)                V1/users#index {:format=>/json/}
                   POST   /V1/users(.:format)                V1/users#create {:format=>/json/}
           V1_user GET    /V1/users/:id(.:format)            V1/users#show {:format=>/json/}
                   PATCH  /V1/users/:id(.:format)            V1/users#update {:format=>/json/}
                   PUT    /V1/users/:id(.:format)            V1/users#update {:format=>/json/}
                   DELETE /V1/users/:id(.:format)            V1/users#destroy {:format=>/json/}
```

<br>

### idを入れたくない場合は、collection

これとは逆に、idを間に入れたくない場合は
collectionを使います。
userコントローラなどで、ファイルを分けずにカスタムアクションとして追加したい場合に使用します

```Ruby:config/route.rb
namespace :V1, format: 'json' do
  resources :users do
    collection do
      get 'name', controller: :users, action: :name
    end
  end
end
```

今回はこれは使用しません。

<br>

## toオプションを使い、controllerとActionを指定

先ほどの`rake routes`の右端、Controller#Actionを見ると
v1/users#user_clubsとなっており、
user_controller.rbのuser_clubsアクションが指定されています。

中間テーブルを扱うので、
Controllerは分けたいところですね。

むしろそうしないと、
user_controllerが長くなってしまいます。

menberの中をtoアクションで
user_clubs_controllerのindexアクションを向くように指定します。

```Ruby:config/route.rb
namespace :v1, format: 'json' do
  resources :users do
    member do
      get 'user_clubs', to: 'user_clubs#index'
      post 'user_clubs', to: 'user_clubs#create'
    end
  end
end
```

```
           Prefix Verb   URI Pattern                        Controller#Action
user_clubs_V1_user GET    /V1/users/:id/user_clubs(.:format) V1/user_clubs#index {:format=>/json/}
                   POST   /V1/users/:id/user_clubs(.:format) V1/user_clubs#create {:format=>/json/}
          V1_users GET    /V1/users(.:format)                V1/users#index {:format=>/json/}
                   POST   /V1/users(.:format)                V1/users#create {:format=>/json/}
           V1_user GET    /V1/users/:id(.:format)            V1/users#show {:format=>/json/}
                   PATCH  /V1/users/:id(.:format)            V1/users#update {:format=>/json/}
                   PUT    /V1/users/:id(.:format)            V1/users#update {:format=>/json/}
                   DELETE /V1/users/:id(.:format)            V1/users#destroy {:format=>/json/}
```

V1/user_clubs#indexとなり、
中間テーブルのuser_clubs_controller.rbを向いていますね。
順調順調。
<br>
### ここで、もう一工夫。URIのuser_clubsを変更したい

もう一工夫加えましょう。
冒頭で話した通り、userの所属するclubなら
user_clubsとするのは冗長です。

URIを
V1/users/:id/user_clubsから
V1/users/:id/clubsに変えたいですね。

URIのネスト変更ではなく、名前を変えるだけなら

アクション以下を
get 'user_clubs'から　
get 'clubs'へ変更するだけで済みます。

```Ruby:config/route.rb
namespace :V1, format: 'json' do
  resources :users do
    member do
      get 'clubs', to: 'user_clubs#index'
      post 'clubs', to: 'user_clubs#create'
    end
  end
end
```

```
       Prefix Verb   URI Pattern                   Controller#Action
clubs_V1_user GET    /V1/users/:id/clubs(.:format) V1/user_clubs#index {:format=>/json/}
              POST   /V1/users/:id/clubs(.:format) V1/user_clubs#create {:format=>/json/}
     V1_users GET    /V1/users(.:format)           V1/users#index {:format=>/json/}
              POST   /V1/users(.:format)           V1/users#create {:format=>/json/}
      V1_user GET    /V1/users/:id(.:format)       V1/users#show {:format=>/json/}
              PATCH  /V1/users/:id(.:format)       V1/users#update {:format=>/json/}
              PUT    /V1/users/:id(.:format)       V1/users#update {:format=>/json/}
              DELETE /V1/users/:id(.:format)       V1/users#destroy {:format=>/json/}
```

URI Patternが
/V1/users/:id/clubs
になっていますね。

スマートです。

<br>

## Controllerをネストしたいなら、scope module、URIだけをネストしたいならscope

さて、ここまでであらかたRESTfulなURIは作れました。
しかしこのままでは悲劇が待っています。

controllerを階層化するのが終わってないからですね。


RESTfulな設計をすればするほど、
controllerの数は増える傾向にありますが、
このままではcontroller/V1配下に大量のコントローラが並べられることになります。

階層を作ってネストさせましょう。

URIを変更せず、Controllerのファイル階層だけを変更するには
scope moduleを使用します。
（逆に、URIをネストさせ、controllerを変更しない場合は、scopeを使用します。）

今回は、V1配下にusersフォルダを作成し、
中間テーブル（user_clubs）を入れることにします。

```Ruby:config/route.rb
namespace :V1, format: 'json' do
  resources :users do
    scope module: :users do
      member do
        get 'clubs', to: 'users_clubs#index'
        post 'clubs', to: 'users_clubs#create'
      end
    end
  end
end
```

```
       Prefix Verb   URI Pattern                   Controller#Action
clubs_V1_user GET    /V1/users/:id/clubs(.:format) V1/users/users_clubs#index {:format=>/json/}
              POST   /V1/users/:id/clubs(.:format) V1/users/users_clubs#create {:format=>/json/}
     V1_users GET    /V1/users(.:format)           V1/users#index {:format=>/json/}
              POST   /V1/users(.:format)           V1/users#create {:format=>/json/}
      V1_user GET    /V1/users/:id(.:format)       V1/users#show {:format=>/json/}
              PATCH  /V1/users/:id(.:format)       V1/users#update {:format=>/json/}
              PUT    /V1/users/:id(.:format)       V1/users#update {:format=>/json/}
              DELETE /V1/users/:id(.:format)       V1/users#destroy {:format=>/json/}
```

Controller#Actionが
V1/users/user_clubs#index と
users配下のuser_clubsに向いていますね！

これでコントローラファイルも、良い感じにネストさせることができました
controllerがごちゃごちゃになるのを防げそうです。


最終形はこんな感じ

```
controller
 ┗ V1
   ┣ users_controller.rb
   ┗ user
    　┗ users_clubs_controller.rb 
```

お題の、userがもつclub情報の取得もこのURIでできそうです。

```
v1/users/:id/clubs 
```


URIだけREST設計っぽく作るなら苦労しなかったんですが、
Controllerのファイルをネストさせたり
URIだけ変更したりするうちに、混ざって詰まりました。

各処理の役割と、書く順番は気をつけましょうというお話です。

### 追記：別の書き方

controllerで使用するcontrollerファイルを
moduleでフォルダを指定すれば、
こんな感じでも書けますね。

ブロック使わない書き方なので、シンプル。

```Ruby:config/route.rb
namespace :V1 do
  resources :users do
    resources :clubs, controller: :user_clubs, module: :users, only: %i[index create]
  end
end
```

<br>

## 最後に、ざっくりまとめ

- 「namespace」は、URIもControllerの格納フォルダも指定する
- 「member」は、idを引き継いだURI作成
- 「collection」は、id引き継がないURI作成
- 「to」オプションで、controllerとアクションの向き先を変更可能
- URIのみネストさせたいときは、「scope」
- controllerの格納フォルダをネストさせるなら、「scope module」

<br>

## 参照リンク集

- [DHHはどのようにRailsのコントローラを書くのか \| POSTD](https://postd.cc/how-dhh-organizes-his-rails-controllers/)

- [ぎんざRuby会議01にて、「マイクロサービス指向 Rails API 開発ガイド」という発表をしました](https://medium.com/finc-engineering/ginzaruby01-rails-api-guide-168fe9cf5b4d)

- [マイクロサービス時代に捧ぐ、Railsでの中規模APIサーバ開発のための技術構成 - Qiita](https://qiita.com/qsona/items/0274c7c1fd27a4a9a545)

- [Railsのルーティングの種類と要点まとめ - Qiita](https://qiita.com/senou/items/f1491e53450cb347606b)

Shallowは、賛否両論あるみたい。

[resources を nest するときは shallow を使うと幸せになれる - Qiita](https://qiita.com/kuboon/items/96bbd227f9497ed81f38)

RESTの思想が汚れるという問題もあるようで

[Railsのroutesのshallowは安易に使わないで欲しい – Matsubo](https://matsu.teraren.com/blog/2019/01/31/rails-routes-shallow-not-recommend/)