👻

簡単に rails API を実装してみた

に公開

Docker

参考

https://zenn.dev/trysmr/articles/b9c99302ebc205

本記事では、web コンテナのポートを 80:3000 で実装している

rails API 実装

cors 設定

Gemfile

+ gem 'rack-cors'

config/initializers/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:3000'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

ログイン機能実装

devise を使用する

ライブラリインストール

Gemfile

gem 'devise'
gem 'devise_token_auth'

User 作成

bin/rails generate devise:install
bin/rails generate devise User
bin/rails generate devise_token_auth:install User auth
bin/rails db:migrate

メール認証設定

今回は使用しない。

config/envioroments/development.rb

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

認証周り実装

各種コントローラー作成

bin/rails g controller auth/registrations
bin/rails g controller auth/sessions

auth/registrations_controller.rb

class Auth::RegistrationsController < DeviseTokenAuth::RegistrationsController
  private
    def sign_up_params
      params.permit(:email, :password, :password_confirmation, :name)
    end
end

auth/sessions_controller.rb

class Auth::SessionsController < ApplicationController
  def index
    if current_api_v1_user
      render json: { is_login: true, data: current_api_v1_user }
    else
      render json: { is_login: false, message: "ユーザーが存在しません" }
    end
  end
end

application_controller.rb

class ApplicationController < ActionController::API
  include DeviseTokenAuth::Concerns::SetUserByToken

  skip_before_action :verify_authenticity_token
  helper_method :current_user, :user_signed_in?
end

config/routes.rb

Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'auth', controllers: {
    registrations: 'auth/registrations'
  }

  namespace :auth do
    resources :sessions, only: %i[index]
  end
end

API 確認

ユーザ作成

curl -X POST http://localhost/auth -d '[name]=test&[email]=test@example.com&[password]=password&[password_confirmation]=password'

ユーザログイン

curl -X POST -v http://localhost/auth/sign_in -d '[email]=test@example.com&[password]=password'

会議室予約管理作成

リソース作成

bin/rails g scaffold Rooms name:string
bin/rails g scaffold Meetings user:references room:references start_at:datetime end_at:datetime
bin/rails db:migrate

config/routes.rb

  resources :rooms
  resources :meetings

curl で確認

会議室登録

curl -X POST -H "Content-Type: application/json" -d '{"room": {"name": "会議室A"}}' http://localhost/rooms

会議登録

curl -X POST -H "Content-Type: application/json" -d '{"meeting": {"user_id": "1", "room_id":"1", "start_at":"2024-01-01 10:00:00", "end_at": "2024-01-01 11:00:00"}}' http://localhost/meetings

リレーション整理

/rooms/:room_id/meetings
room_id で絞り込まれた meetings のレコードが返ってくるようにする

models/room.rb

class Room < ApplicationRecord
  has_many :meetings
end

models/meeting.rb

class Meeting < ApplicationRecord
  belongs_to :room
  belongs_to :user
end

config/routes.rb

  resources :rooms do
    resources :meetings, only: [:index]
  end

meeting_controller.rb

  # GET /meetings
  def index
-   @meetings = Meeting.all
+   @meetings = Meeting.where(where_params)

    render json: @meetings
  end

...
    # Only allow a list of trusted parameters through.
    def meeting_params
      params.require(:meeting).permit(:user_id, :room_id, :start_at, :end_at)
    end

+   def where_params
+   params.permit(:room_id, :start_at, :end_at)
+   end

既にその日時で予約がとられている場合は予約できないようにする
meetings_controller.rb

class MeetingsController < ApplicationController
  before_action :set_meeting, only: [:show, :update, :destroy]

  # GET /meetings
  def index

    if where_params[:room_id].present?
      @meetings = Meeting.where(room_id: where_params[:room_id])
    else
      @meetings = Meeting.all
    end

    if where_params[:start_at].present?
      @meetings = @meeting.where(start_at: where_params[:start_at]...)
    else
      current_time = Time.zone.now
      rounded_time = current_time.change(hour: 0, min: 0, sec: 0)
      @meetings = @meetings.where(start_at: rounded_time...)
    end

    if where_params[:end_at].present?
      @meetings = @meeting.where(start_at: ...where_params[:start_at])
    end

    render json: @meetings
  end

  # GET /meetings/1
  def show
    render json: @meeting
  end

  # POST /meetings
  def create
    @meeting = Meeting.new(meeting_params)
    is_not_exists = Meeting.where(
      "(start_at >= ? AND start_at < ?) OR (end_at > ? AND end_at <= ?)",
      meeting_params[:start_at], meeting_params[:end_at], meeting_params[:start_at], meeting_params[:end_at]
    ).where(room_id: meeting_params[:room_id]).empty?

    if is_not_exists && @meeting.save
      render json: @meeting, status: :created, location: @meeting
    else
      render json: @meeting.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /meetings/1
  def update
    if @meeting.update(meeting_params)
      render json: @meeting
    else
      render json: @meeting.errors, status: :unprocessable_entity
    end
  end

  # DELETE /meetings/1
  def destroy
    @meeting.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_meeting
      @meeting = Meeting.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def meeting_params
      params.require(:meeting).permit(:user_id, :room_id, :start_at, :end_at)
    end

    def where_params
      params.permit(:room_id, :start_at, :end_at)
    end
end
GitHubで編集を提案

Discussion