ずみーBlog

元クラゲ研究者(見習い)の92年生まれがエンジニアを目指しながら日々寄り道するブログです。

RailsのAPIモードでアプリケーション開発してみる 〜バックエンドのユーザ管理機能編〜

エンジニア目指して独学中なのですが、いろんな技術をリサーチしていく中で、こんな構成でアプリを作ってみたくなりました。

DB MySQL
バックエンド RailsAPIモード
フロントエンド React
デザイン Semantic UI React

そんなわけで、今日はRailsAPIモードでアプリケーションを作り、ユーザと投稿だけの最小限のCRUDアプリを実装していこうと思います。

Rails newの--apiオプション

Railsのバージョン6.0.0で、DBはMySQLを指定しつつ、---apiオプションでAPIモードのRailsプロジェクトを立ち上げます。

rails _6.0.0_ new try_api_react -d mysql --api

DeviseとDevise Token Auth導入

ユーザ管理に必要なGemを入れていきます。私の場合、Deviseのみでユーザテーブルを先に作ってしまっていたので、こちらの記事が参考になりました。

Gemfile

# Deviseを使ったUserテーブルがすでにある前提
gem 'devise_token_auth'
bundle install
rails g devise_token_auth:install User auth

ユーザテーブルがすでに存在するので、Devise Token Authによって作成されたマイグレーションファイルを一旦削除し、カラム追加用のマイグレーションを作成します。(公式ガイド参照

rails g migration AddTokensToUsers provider:string uid:string tokens:text

マイグレーションファイルの中身は公式ドキュメントのものをそのまま。

class AddTokensToUsers < ActiveRecord::Migration[6.0]
# create migration by running a command like this (where `User` is your USER_CLASS table): 
  # `rails g migration AddTokensToUsers provider:string uid:string tokens:text`

  def up
    add_column :users, :provider, :string, null: false, default: 'email'
    add_column :users, :uid, :string, null: false, default: ''
    add_column :users, :tokens, :text

    # if your existing User model does not have an existing **encrypted_password** column uncomment below line.
    # add_column :users, :encrypted_password, :null => false, :default => ""

    # the following will update your models so that when you run your migration

    # updates the user table immediately with the above defaults
    User.reset_column_information

    # finds all existing users and updates them.
    # if you change the default values above you'll also have to change them here below:
    User.find_each do |user|
      user.uid = user.email
      user.provider = 'email'
      user.save!
    end

    # to speed up lookups to these columns:
    add_index :users, [:uid, :provider], unique: true
  end

  def down
    # if you added **encrypted_password** above, add here to successfully rollback
    remove_columns :users, :provider, :uid, :tokens
  end
end
rails db:migrate

Devise Token Authの各種設定

ルーティング

/api/v1/のパスでDevise Token Authが使えるように設定します。

Rails.application.routes.draw do
  devise_for :users
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  namespace :api do
    scope :v1 do
      mount_devise_token_auth_for 'User', at: 'auth'
    end
  end
end

リクエスト毎にアクセストークンが変わらないようにする

change_headers_on_each_requestの行のコメントアウトを外し、値をfalseにします。

# frozen_string_literal: true

DeviseTokenAuth.setup do |config|
  # 省略
  config.change_headers_on_each_request = false
  # 省略
  # config.enable_standard_devise_support = false
end

最後の行はもともと入れていたDeviseに何か関係がありそうですが、今回はデフォルトでコメントアウトされていました

CORSを許可する

こちらの記事公式ドキュメントを参考に、CORSを許可していきます。

Gemfile

gem 'rack-cors', :require => 'rack/cors'
bundle install

config/initializers/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3000'
    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

Reactの開発をポート8000番で行う場合は、許可するオリジンはlocalhost:8000とすればいいのでしょうか。

POSTリクエストでユーザ登録してみる

それでは、CORS許可したポート3000番でサーバを立ち上げ、Advanced REST clientなどを使ってPOSTリクエストを投げて見ます。

rails s -p 3000

リクエスト内容

HTTPメソッド POST
URL http://localhost:3000/api/v1/auth
Header Content-Type: application/json
Body {"email":"example222@example.com","password":"password","password_confirmation":"password"}

すると、ユーザ登録が成功し、以下のようなレスポンスが返ってきました。

Response Header

x-frame-options:
SAMEORIGIN
x-xss-protection:
1; mode=block
x-content-type-options:
nosniff
x-download-options:
noopen
x-permitted-cross-domain-policies:
none
referrer-policy:
strict-origin-when-cross-origin
access-token:
WyW3o9LlV0ymwkPuKx84VQ
token-type:
Bearer
client:
P6LapCU5aaSKqLPO7JYAKA
expiry:
1607828063
uid:
example222@example.com
content-type:
application/json; charset=utf-8
etag:
W/"f800f46c772f1529d26e671e3b8f2af4"
cache-control:
max-age=0, private, must-revalidate
x-request-id:
e0803dc4-db45-44ea-a160-8d2f1af7d46f
x-runtime:
0.278159
vary:
Origin
transfer-encoding:
chunked

Response Body

{
  "status": "success",
  "data": {
    "id": 3,
    "email": "example222@example.com",
    "created_at": "2020-11-29T02:54:23.394Z",
    "updated_at": "2020-11-29T02:54:23.442Z",
    "provider": "email",
    "uid": "example222@example.com"
  }
}