方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

Grapeを用いたRails APIの構築:Reactフロントエンドからの呼び出し方法

grape-rails-api
この記事は約12分で読めます。

 本記事では、RailsでAPIを構築する際にGrapeを使うメリットや実装手順を解説

RailsのコントローラーのみによるAPI構築の限界

ビューとAPIレスポンス(Json)の混在

RailsのコントローラーとモデルだけでAPIを構築することは可能だが、以下のような制限や課題がある。

RailsのコントローラーはHTMLビューをレンダリングすることを前提に設計されており、API専用に設計されていないため、API用のロジックを追加した場合、ビューとレスポンスの混在という問題が発生する。

Ruby
class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render :index }
      format.json { render json: User.all }
    end
  end
end

こうしたコードは小規模なアプリケーションでは問題ないが、規模が大きくなると管理が難しくなる。またAPIのバージョニングを手動で実装する必要があり、バージョニングの管理が困難になる。

 

APIバージョニング手動管理の煩雑さ

APIのバージョニング(v1, v2など)を導入して管理するのは、既存のクライアント(アプリケーションやユーザー)に影響を与えずにAPIの仕様を変更する必要がある場合。

例えば、ユーザー情報を返すAPIレスポンスの場合で、v2ではv1では返していなかった追加の情報(ageaddress)が必要になり、レスポンス形式を変更する場合、v1のクライアントは新しいフィールドを想定していないため、互換性のない変更になる。なので、v1を維持しつつ、v2として新しい仕様を導入することで、互換性を保ちながら移行可能になる。

上記のような課題を解決する方法としてGrapeというライブラリがある。

 

Grape: Rails API専用に設計されたDSL

GrapeはRailsAPI専用に設計されたDSL(ドメイン固有言語:特定の作業や問題解決を目的に設計されたプログラミング言語)で、RESTfulなAPIを効率的に構築できる。

https://github.com/ruby-grape/grape

Grapeが適しているケース

  • サーバーサイドをAPI専用として利用する。
  • モバイルアプリやフロントエンド(React/Vue.js)からのJSONリクエストを主に処理する。
  • APIバージョン管理が必要。
  • RESTfulな設計を重視したい。

Grapeファイル構成例

Ruby
app/
├── api/
│   ├── base_api.rb              # Grape APIのベースクラス
│   ├── app_api/
│   │   ├── v1/
│   │   │   ├── users/
│   │   │   │   ├── data.rb       # ユーザー関連のデータ取得API
│   │   │   │   ├── profile.rb    # ユーザープロファイル関連のAPI
│   │   │   ├── root.rb           # V1 APIのエントリーポイント
│   │   ├── v2/
│       ├── tasks/
│       │   ├── management.rb     # タスク管理のAPI
│       ├── root.rb               # V2 APIのエントリーポイント
config/
└── routes.rb                     # APIをマウントするルート設定

 


Grape実装例

app/api/base_api.rb

全APIで共通の処理を定義するクラス。

Ruby
class BaseAPI < Grape::API
  format :json
  prefix :api

  helpers do
    def current_user
      # 認証トークンから現在のユーザーを取得
      User.find_by(auth_token: headers['Authorization'])
    end
  end

  rescue_from ActiveRecord::RecordNotFound do |e|
    error!({ message: 'Resource not found', details: e.message }, 404)
  end

  rescue_from :all do |e|
    error!({ message: 'Internal server error', details: e.message }, 500)
  end
end

 

app/api/app_api/v1/users/data.rb

ユーザー関連のデータ取得エンドポイント。

Ruby
# app/api/app_api/v1/users/data.rb
module AppAPI
  module V1
    module Users
      class Data < Grape::API
        resource :users do
          # GETリクエスト: ユーザー一覧を取得
          desc 'Get all users'
          get do
            present User.all, with: Entities::User
          end

          # POSTリクエスト: 新しいユーザーを作成
          desc 'Create a new user'
          params do
            requires :name, type: String, desc: 'User name'
            requires :email, type: String, desc: 'User email'
          end
          post do
            user = User.create!(declared(params))
            present user, with: Entities::User
          end
        end
      end
    end
  end
end

 

app/api/app_api/v1/root.rb

app/api/api/v1/root.rbを作成し、v1のAPI全体を管理する

Ruby
class AppAPI::V1::Root < Grape::API
  mount AppAPI::V1::Users::Data
end

config/routes.rb

APIをマウントするルート設定。これにより、/api/v1/usersなどのエンドポイントが有効になる

Ruby
Rails.application.routes.draw do
  mount AppAPI::V1::Root => '/api/v1'
  mount AppAPI::V2::Root => '/api/v2'
end

 

 

フロントエンド(React)からAPIエンドポイントを呼び出す

以下は、ReactでAxiosを使用して/api/v1/usersエンドポイントを呼び出す例。

Axiosインスタンスの作成

APIのベースURLを設定したAxiosインスタンスを作成する。

src/api/axiosInstance.js

TypeScript
import Axios from 'axios';

const axiosInstance = Axios.create({
  baseURL: '/api/v1', // APIのベースURL
  headers: {
    'Content-Type': 'application/json',
  },
});

export default axiosInstance;

 

APIリクエストの関数作成

GETリクエストとPOSTリクエストを行う関数を定義する。

src/api/usersApi.js

TypeScript
import axiosInstance from './axiosInstance';

// ユーザー一覧を取得
export const fetchUsers = async () => {
  try {
    const response = await axiosInstance.get('/users');
    return response.data; // サーバーから返されたデータ
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
};

// 新しいユーザーを作成
export const createUser = async (user) => {
  try {
    const response = await axiosInstance.post('/users', user);
    return response.data; // 作成されたユーザー情報
  } catch (error) {
    console.error('Error creating user:', error);
    throw error;
  }
};

 

 

Reactコンポーネントでの利用

Reactコンポーネント内でfetchUserscreateUserを使用する。

src/components/UserList.js

TypeScript
タイトルとURLをコピーしました