この記事はプログラミング学習者がアプリ開発中に躓いた内容を備忘録として記事におこしたものです。内容に不備などあればご指摘頂けると助かります。
今回はRails(API) x ReactでX(旧Twitter)のクローンサイトを作る際に実装した新規登録機能について実装中に多々躓いた所があったので、備忘録を兼ねて記事にしていくことにしました。
0. 開発環境
今回は以下の開発環境で実装しています。
言語など | バージョン |
---|---|
PC | Mac |
Rails | 7.0.6 |
Ruby | 3.2.1 |
React | 19.1 |
1. devise_auth_tokenとは?
Railsの認証ライブラリであるDeviseを拡張して、トークン認証で動くライブラリです。Deviseはセッションでの認証となりますので、認証方式が異なります。
APIモードやSPA(React/Vue)・モバイルアプリ用のバックエンド向けとなります。
2. ユーザー登録実装の流れ
1, フロントエンド側(React)でPOSTリクエストを使って指定のアドレスに必要な登録情報(Email, 電話番号, パスワードなど)を送信します。
2, バックエンド側(Rails)でデータを受け取り、DBにデータを登録します。
3, データ登録後、confirmationメールをサーバー側からクライアント側へ擬似的に送信
3. フロントエンド側でデータの送信
import axios from "axios";
const handleSignUp = async () => {
try {
const response = await axios.post("http://localhost:3000/api/v1/users", {
phone_number: user.phone_number,
email: user.email,
birthday: user.birthday,
password: user.password,
password_confirmation: user.password_confirmation,
confirm_success_url: "http://localhost:5173",
});
console.log(response.data);
setUser(defaultUser);
} catch (error) {
console.error(error.response.data.errors);
}
};
axiosはPromiseベース(非同期)で動くHTTPクライアントです。
要はHTTP通信を行うためのライブラリを意味します。
上のコードではaxiosを使ってHTTP通信のうちPOSTメソッドを実行することにより後に続くパラメーター(emailなど)をバックエンド(Rails)側に送信しています。
4. devise_auth_tokenの導入
ここでは実装に必要なgemをインストールします。
gem 'devise'
gem 'devise_token_auth'
gem 'rack-cors'
上記のgemをGemfileに追記したら、実際にプロジェクト内へインストールしていきましょう。
bundle install
私の場合はDockerで実装していたので、下記のコマンドになります。
dokcer compose exec api(サービス名) bundle install
次に初期設定ファイルなどを生成していきます。
rails g devise:install
docker compose exec api(サービス名) rails g devise:install
rails g devise_token_auth:install User users
docker compose exec api(サービス名) rails g devise_token_auth:insall User users
2つのコマンドを実行することでdeviseとdevise_token_authの導入に必要な初期設定用のファイルを生成されます。
rails g devise_token_auth:install User users
のusersの部分はルートパス(エンドポイント)を指定しています。
/users/sign_in
/users/sign_out
users
などのエンドポイントになります。
5. Usersテーブルを作成する。
rails g devise_token_auth:install User auth
を実行して生成されたマイグレーションファイルについて、必要な所があれば修正(null: falseの追記など)。
class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[7.0]
def change
create_table(:users) do |t|
## Required
t.string :provider, null: false, default: 'email'
t.string :uid, null: false, default: ''
## Database authenticatable
t.string :encrypted_password, null: false, default: ''
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.boolean :allow_password_change, default: false, null: false
## Rememberable
t.datetime :remember_created_at
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
## User Info
t.string :name, null: false, default: '', unique: true, limit: 50
t.string :profile, limit: 160
t.string :location
t.string :website
t.string :birthday, null: false
t.string :email, null: false, unique: true
## Tokens
t.json :tokens
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, %i[uid provider], unique: true
add_index :users, :reset_password_token, unique: true
add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
修正が終わればrails db:migrate
でテーブルを作成する。
6. APIコントローラーの作成
次に新規登録用のコントローラーを作成していきます。
rails g controller api/v1/registrations
docker compose exec api(サービス名) rails g controller api/v1/registrations
ストロングパラメーターで新規登録時に登録できるカラム名を指定します。
コントローラー内にはストロンゴパラメーターしか記述されていませんが、
DeviseTokenAuth::RegistrationsController
を継承しているため、
ユーザー登録などの処理はdevise_token_authが全て裏で処理しています。
module Api
module V1
class RegistrationsController < DeviseTokenAuth::RegistrationsController
private
def sign_up_params
params.require(:registration).permit(:name, :email, :phone_number, :birthday, :password, :password_confirmation)
end
end
end
end
7. ルーティング設定
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
mount_devise_token_auth_for 'User', at: 'users', controllers: {
registrations: 'api/v1/registrations'
}
end
end
end
ここではエンドポイントとそにれ対して作動するコントローラーを指定しています。
mount_devise_token_auth_for 'User', at: 'users'
のところで
users
の部分でURLのエンドポイントを指定しています。
上記コードの場合、ap1/v1/users
というパス(エンドポイント)にアクセスが来た時、
devise_token_authのregistrations系のリクエスト(新規登録など)は
app/controllers/api/v1/registrations_constroller.rbを使うように設定しています。
全てのリクエストがregistrations_controller.rbにいくわけではありません。
api/v1/users
へのPOST(新規登録)などregistrationsに該当するリクエストのみが対象。
前述のフロントエンド側の操作でHTTPメソッドのPOSTでAPI側にアクセスするように実装しています。
フロントエンド側で指定したhttp://localhost:3000/api/v1/users
にアクセスするとroutes.rb
に指定されたルーティングによりregistrations_controllerへ導かれます。
8. CORSに関する説明と設定
- CORSとは
Cross-Origin Resource Sharingの略で異なるオリジンからのリソース(Webページなど)へのアクセス制限を緩和する仕組みです。
オリジンとはプロトコル、ドメイン、ポート番号を併せたものを指します。
オリジン例:https://qiita.com:80
上記のオリジンで説明しますと、
プロトコル:https
ドメイン:qiita.com
ポート番号:80
言い換えますと、異なるURLからリソースへのアクセスを許可する仕組みとなります。
今回私が作成しているX(旧Twitter)のクローンサイトは下記の構成になっています。
フロントエンド(React):http://localhost:5173
バックエンド(Rails) :http://localhost:3000
※オリジン(ほぼURL)が異なる事に着目
ユーザーの新規登録機能はフロントエンド側で入力したデータをバックエンド側にアクセスしてデータベースに保管する作業となります。
異なるオリジン間のリソース共有が発生するので、ここでCORSの出番となります。
以下はCORSの設定です。
また、冒頭でインストールしたrack-cors'
もここで使用することとなります。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:5173' # 許可するオリジンの指定
resource '*',
headers: :any,
expose: %w[access-token expiry token-type uid client], #追記
methods: %i[get post put patch delete options head]
end
end
origins
にフロントエンド側のURLを記述することでアクセスが可能になります。
初期設定ではorigins *
に設定されているので、どのオリジンからもアクセスできる設定になっています。指定のオリジンに書き換える事で他のオリジンからのアクセスは不可となり、ホワイトリストの役割を果たします。
追記部分のexpose
があることによってフロントエンド側が認証情報を取得できるようになります。
この記述が無い場合は認証情報が不十分という理由により401エラーが発生する可能性がありますのでご注意ください。
9. confirmableを有効にする
新規登録のために必要項目を入力した後に確認メールを登録用メールアドレスに送信する設定です。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
include DeviseTokenAuth::Concerns::User
end
モデルのuser.rb
でconfirmableは初期状態ではコメントアウトされているので、deviseの使用機能として追加します。
10. letter_opener_webの設定
上記の確認メール設定で送信されたメールが正しく送信されているか、開発環境でチェックするための設定を行います。
- letter_opener_webのインストール
group :development do
gem 'letter_opener_web', '~> 3.0'
end
letter_opener_webは開発環境だけで使用するので、開発環境グループに追加してインストールします。
- routesの編集
mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?
routes.rbに上記コードを追加することで /letter_opener
のエンドポイントにアクセスした時に開発環境限定でletter_opener_webのアプリケーションを実行できます。
11. developmen.rbの設定
letter_opener_webが開発環境下で使えるようにdevelopment.rb
に追記します。
# 電子メールが配信完了できない場合にエラーを発生させるかの設定
config.action_mailer.raise_delivery_errors = true
# フラグメントキャッシュを利用するための設定
config.action_mailer.perform_caching = false
# 送信されるメール内のURLのドメインが`localhost`で固定される
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# メールの配信方法を設定
config.action_mailer.delivery_method = :letter_opener_web
# メール配信を行うかどうかの設定
config.action_mailer.perform_deliveries = true
※フラグメントキャッシュ:Webページの断片(部分テンプレートなど)をキャッシュし、リクエストごとに毎回生成するのではなく、キャッシュされたものを再利用することでパフォーマンスを向上させる機能。
12. 参考にしたサイト
letter_opener_web Github
letter_opener_web設定
なんとなく CORS がわかる...はもう終わりにする。