Ruby on RailsのActive StorageをAPI モードで使うなら絶対検討すべきこと
Ruby on Rails の 5.2 から標準で入った Active Storage。
Rails 5.2 が公式リリースされる前から本番登用していたので、使い始めてからそろそろ使用してから1年が経つ。
現在仕事で関わっているサービスでは Rails は API サーバーとして運用しているが、Acitve Storage をファイルを中央集権的に管理するように使ったことでとても幸せになれているのでその知見を書いておきたい。
API サーバーとして Rails を使っていて、Active Storage の導入を考えている方はぜひ一度この使い方を検討してみてほしい、
※ この使い方に関しては、Active Storage に限らず carrierwave とかでも活かせるかもしれない。
Active Storage とは
Active Storage とはどんなものかは公式のドキュメントを見るほうが早いと思う。
中央集権管理とは
これは造語的に使っているので、どんなものなのかを説明する。
マニュアルとおり使うときっとこうなる
画像などのファイルを紐づけたいモデルに対して、Active Storage によるアップロード機構を付けていくことになるはずである。そうなると、違うモデルにまたファイルを紐づけたいときには、同じことをそのモデルに対しても行うことになる。
一方で中央集権管理的とは
中央集権管理的とは、このように Active Storage と紐づくだけのモデルを1つ作り、他のモデルからこのモデルを参照するイメージ。
こうすることで、追加的にファイルを使いたいモデルができたとしても、Acitve Storage のファイルを管理しているモデルへの参照をつけるだけ。
これは、いつもとおりの Rails の外部参照なのでなんの問題もなくできるのがよいところ。
中央集権管理のメリット
まず、Active Storage 特有の処理が1つのモデルに集約されるので見通しがだいぶよい。
さらに、ユーザーがアップするファイルがここに集まっているので、監視がしやすい。
規約違反のものなどをチェックする必要のあるサービスには大きなメリットかもしれない。
また、同じファイルを複数のモデル・レコードで使い回せるのはよい。参照先の id を変えればすぐに参照を切り替えられる手軽さは大きい(中央集権管理していないと同様のファイルをアップロードし直さないとファイルを張り替えられない)
データの更新とファイルアップロードのタイミングがわかりやすい
もう1つメリットとしては、データの更新とファイルのアップロードのタイミングがわかりやすいという点。
マニュアルとおりの場合
次のような更新の仕方だと、通常の update と画像の変更のタイミングが違ってくる。
class Api::UsersController
def update
image_file = params[:image]
@user = User.find(params[:id])
@user.update!(user_params)
## ↓ ここで例外発生した場合どうする?
@user.image.attach(image_file)
end
end
Rollback をするのか、画像だけアップロードに失敗したということにするのか決めれば問題ないのだが、すべてのモデルで同じ挙動をするかは実装者によってしまうので意図せぬバグを埋めてしまいそう。
中央集権管理の場合
画像をアップロードする処理とその他の update を分けられるのでよい。
class Api::ImagesController
def create
image_file = params[:image]
@image = Image
.create!(images_params)
@image.image.attach(image_file)
end
end
上の変更が成功してから id を入れて普通の update 処理をすれば見通しが良くなる。
class Api::UsersController
def update
image_id = params[:image_id]
@user = User.find(params[:id])
@user.update!(image_id: image_id)
end
end
まとめ
API サーバーとして Rails を使っている場合、中央集権管理にしたほうがだいぶ見通しが良くなると思っている。
Rails で html まで返す従来のアプリケーションであれば、それぞれのモデルに Active Storage を紐づけたほうが 1 回のリクエストで更新処理が行う都合上いい気がする。
しかし、API サーバーとして運用しているのであれば、その限りではないはず。あまり見ない使い方ではあるが、ぜひ一度検討してみるとよいかもしれない。