💭

React + Rails API モードで基本的な CRUD アプリを作ってみた (バックエンド編)

2020/10/18に公開
1

はじめに

世は大 SPA 時代!!
「そろそろフロントもやっておくか」ということでReact の公式チュートリアルをやりました。

が、実際のアプリケーションではバックエンドとAPIレベルで通信することは必須。
簡単な CRUD 機能を持つバックエンドをRailsの API モードを使って実装し、はじめての SPA に挑戦してみることにしました。

初心者なので至らない部分も沢山あると思いますが、「Rails API + React フロントエンド」という実装についてイメージできるようになっていると思います。
今回は最小限の構成(モデル1つ、コントローラ1つ)ですが、基礎を抑えておけばスケールすることは十分可能だと思います。

それではやっていきましょう🙋‍♂️

No Title
1 バックエンド編 ← 今ここ
2 フロントエンド編 その1
3 フロントエンド編 その2
4 その他考察編

作ったアプリ

ポストを投稿できるだけのシンプルなアプリです。
ポストに対して CRUD 処理を実装していきます。


環境、リポジトリ

Name Version
node.js 12.19.0
React 16.13.1
Ruby 2.6.6
Rails 6.0.3.4
Docker for Mac 2.4.0.0
Name URL
フロントエンド https://github.com/tatsuro-m/react_rails_api_frontend
バックエンド https://github.com/tatsuro-m/react_rails_api_backend

アプリの構成はどのようにするか

ネット上で記事を確認していると、 rails new したアプリルート直下に frontend などのディレクトリを作ってその下に create-react-app でReact 部分を作るというのを良く見ました。
しかし個人的にはこのディレクトリ構成はしっくりきません。

「完全に別々のアプリなんだ」ということをハッキリするためにプロジェクト、リポジトリごと別々に分けました。
結果、今のところこれで良かったと思っています。

Name URL
フロントエンド https://github.com/tatsuro-m/react_rails_api_frontend
バックエンド https://github.com/tatsuro-m/react_rails_api_backend

こうした方が別々のライフサイクルでアプリを動かすことができる気がします。
今回はそこまで関係ありませんが、別のプロジェクトとしておくと、

  • コンテナ基盤で別々のインフラスケーリングがやりやすい
  • コミットがフロントとバックエンドで混ざらない
  • 動作の切り分けがより直感的
  • CI/CD パイプラインも別々にしやすい

などのメリットがありそうです。
この辺はお好みですね😁

環境構築

  • バックエンド → Docker を使ってサクッと立ち上げます
  • フロントエンド → Mac にインストールした node.js を使います(Docker 使っても良いのですが、 node_modules 配下が重いですし、こっちの方が楽でした)

詳しくは、非常に簡単ですが README に手順が書いてあるので参考にしてください!
基本的に、

  1. api モードで rails new する
  2. create-react-app でReact アプリ作成

ぐらいです。。。

バックエンド

Docker で環境が立ち上がったらバックエンドはサクッと片付けてしまいましょう。

コントローラー

rails g controller posts します。
中身は割とお作法通りの、

class PostsController < ApplicationController
  before_action :set_post, only: %i[show destroy update]

  def index
    posts = Post.all.order(:id)
    render json: posts
  end

  def show
    render json: @post
  end

  def create
    post = Post.new(post_params)
    if post.save
      render json: post
    else
      render json: post.errors
    end
  end

  def update
    if @post.update(post_params)
      render json: @post
    else
      render json: @post.errors
    end
  end

  def destroy
    if @post.destroy
      render json: @post
    else
      render json: @post.errors
    end
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  def post_params
    params.require(:post).permit(:title, :content)
  end
end

となっております!!

注意点としては、

  • 基本的に実装するアクションは「フロントにデータを返す」アクションだけ
  • API レベルで通信するので、いつものように view をレンダリングするのではなく、 render json: {data} で返す。
    ぐらいでしょうか。

必要なアクションは作りたい形によって若干変わるのですが、ビューの部分は完全にReact に任せるのが前提なので、 newedit アクションは不要です。データを返すわけでは無いからです。
show も今回は入っていますが、不要な場合もあると思います。

ストロングパラメータはいつも通り使うようにしましょう😁

ルーティングの設定をお忘れなく!

モデル

rails g model Post します。
今回はシンプルに、

  create_table "posts", force: :cascade do |t|
    t.string "title", null: false
    t.text "content", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

こんなスキーマでやってみました。
titlecontent があるだけのモデルです。
ポストを投稿できるアプリですね。

seed を流しておくと動作確認が楽です。
rails db:seed

curl で確認しておきましょうか。

curl -X GET http://localhost:3001/posts | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3620    0  3620    0     0  60333      0 --:--:-- --:--:-- --:--:-- 60333

[
  {
    "id": 59,
    "title": "サンプル0",
    "content": "これはseedで作成したものです。",
    "created_at": "2020-10-18T04:34:49.594Z",
    "updated_at": "2020-10-18T04:34:49.594Z"
  },
  {
    "id": 60,
    "title": "サンプル1",
    "content": "これはseedで作成したものです。",
    "created_at": "2020-10-18T04:34:49.608Z",
    "updated_at": "2020-10-18T04:34:49.608Z"
  },

json でデータが返ってくれば成功です!!

これでバックエンドは基本的に終わりなのですが、1点注意を。
API モードのRailsが異なるオリジン(この場合はフロントのReact アプリ)からアクセスを受けるためには明示的にアクセスを許可する必要があります。
gem 'rack-cors' をコメントインして(new した時点で Gemfile にコメントアウトされて入っています)、 bundle install

設定を追記しておきましょう。

application.rb
~~~
    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins ENV["FRONTEND_ORIGIN"]
        resource "*",
                 headers: :any,
                 methods: [:get, :post, :patch, :delete, :options, :head]
      end
    end

これで再起動すればフロントからのアクセスができるはずです。
ちなみに、環境変数 FRONTEND_ORIGIN は、docker-compose.yml に記載されています。

FRONTEND_ORIGIN: http://localhost:3000

オリジンは環境によって分けることが多いと思いますので、環境変数を使っています。


バックエンド編はここまで!
次回はいよいよReact アプリを構成していきます🙌


PS. やっぱりRailsって開発自体はすごく早いですよね🧐

次回もお楽しみに!!

Discussion

RokuinuRokuinu

すごく丁寧な解説で、Rails API + Reactの構成がイメージしやすいですね!バックエンドとフロントエンドの切り分けが、スケーラビリティやCI/CDにも有利という点、とても納得です。

APIの実装を行うなら、APIテストツール『EchoAPI』も使ってみると便利かもしれません。オフラインでもVSCode内でAPIテストができ、操作が簡単で初心者にも優しいです。特に、Rails APIの開発中に手軽にAPI通信を確認できるので、作業がさらにスムーズになります。