【Rails】RailsのAPIモード

# 前書き
プログミングスクールに通い始めて2ヶ月ほどが経過しました。
そんな私が、railsを学習する中で気になった技術を見たので実際に実装も兼ねて、学習内容を記事にします。
この記事は、rails APIとは？となっている初学者に向けた記事です。
自分への復習としても兼ねております。

## Rails APIモードとは？
RailsのAPIモードは、WebアプリケーションフレームワークRuby on Railsの軽量バージョンで、APIサーバーとしての開発に特化したモードです。通常のRailsアプリケーションでは、ビュー（HTMLやCSS）やフロントエンドのテンプレートエンジンが含まれていますが、APIモードではこれらが不要となるため、効率的で軽量なAPI開発が可能になります。
### 特徴
##### 1. ビュー関連の不要な機能が省略
APIモードでは、HTMLテンプレートやJavaScript、CSSを扱う機能が省略され、JSON形式でデータを返すAPI開発に特化しています。
ActionController::APIを継承したコントローラーを利用するため、余計な機能が削除されています。
##### 2.軽量で高速
ビューやフロントエンドに関連する部分が排除されているため、メモリ消費が少なく、リクエストの処理速度も向上します。
#####  3.JSONデータをデフォルトで返す
APIモードでは、レスポンスとしてJSONデータを返すのが標準的な設定になっています。クライアント（例：React、Vue.js、モバイルアプリなど）にデータを渡す際に便利です。
##### 4.専用のジェネレーター
rails newコマンドに--apiオプションを付けてプロジェクトを作成すると、APIモード専用の設定が適用されます。
・ビュー関連のGemや設定が省略される。
・ActionController::APIを基盤としたコントローラーが生成される。
```
rails new my_api_app --api
```
```rb
#通常のコントローラー
class ApplicationController < ActionController::Base
end
```
```rb
#APIモードのコントローラー
class ApplicationController < ActionController::API
end
```
## 主な利点

##### 1.ビューが不要な場合に便利
ReactやVue.jsなど、フロントエンドが別途用意されているプロジェクトでは、RailsをAPI専用として利用できます。
##### 2.バックエンドの専用サーバーに最適
モバイルアプリやフロントエンドアプリケーションにデータを提供するAPIサーバーとして最適です。
##### 3.コードがシンプル
必要最低限の機能しか含まれないため、コードがシンプルになります。

## 使い方例
【前提】
```
project
   |-----frontディレクトリ
   |        |-----index.html
   |        |-----script.js
   |
   |-----railsディレクトリ(rails new アプリ名 --apiで作成したディレクトリ)
```
・Userモデル 
nameカラム  emailカラムを設定しています。

【設定】
```rb:config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "*"

    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```
このファイルは--apiで作成した際にデフォルトで作成されているはずです。
コメントアウトされているので上記の部分のコメントアウト部分を外します。
記述内容の詳細は省きますが、ドメインやリソース等の設定行っています。
今回は　"*"　とすることで、全てを許可する設定にしています。

```rb:routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users, only: [:index, :show, :create, :update, :destroy]
    end
  end
end
```
名前空間を使ってネストすることで将来的に異なるバージョンのAPIをサポートする際に、バージョンごとに異なるルートを定義することができます。
GET /api/v1/users (index)
GET /api/v1/users/:id (show)
POST /api/v1/users (create)
PUT /api/v1/users/:id (update)
DELETE /api/v1/users/:id (destroy)
この設定により、API のバージョン 1 において、ユーザーリソースに対する基本的な CRUD 操作がサポートされます。

```rb:app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
  def index
    users = User.all
    render json: users
  end

  def show
    user = User.find(params[:id])
    render json: user
  end

  def create
    user = User.new(user_params)
    if user.save
      render json: user, status: :created
    else
      render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def update
    user = User.find(params[:id])
    if user.update(user_params)
      render json: user
    else
      render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def destroy
    user = User.find(params[:id])
    user.destroy
    render json: { message: 'User deleted successfully' }
  end

  private

  def user_params
    params.require(:user).permit(:name, :email)
  end

end
```
おおよそ通常railsのCRUD操作の記述内容と同じにしています。
`render json: users` は、users 変数に格納されているデータを JSON 形式に変換し、それを HTTP レスポンスのボディとしてクライアントに送信します。これにより、クライアントはユーザー情報を JSON 形式で受け取ることができます。
`status: :created` は、リソースの作成が成功したことを示すために使用されるステータスコードです。
`json: { errors: user.errors.full_messages }` は、user オブジェクトのエラーメッセージを JSON 形式で返すことを示しています。`user.errors.full_messages` は、user オブジェクトのバリデーションエラーのメッセージを配列として取得します。これにより、クライアントはどのフィールドがどのようにバリデーションに失敗したのかを理解することができます。

### フロントエンドの記述

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Rails API Frontend</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>User List</h1>
  <ul id="user-list"></ul>

  <h2>Add User</h2>
  <form id="add-user-form">
    <input type="text" id="user-name" placeholder="Name" required>
    <input type="email" id="user-email" placeholder="Email" required>
    <button type="submit">Add User</button>
  </form>

  <script src="script.js"></script>
</body>
</html>
```
scriptタグでscipt.jsを読み込んでいます。

```js:script.js
const apiBaseUrl = 'http://localhost:3000/api/v1/users'; 
// Rails APIのURL

// ユーザー一覧を取得して表示
async function fetchUsers() {
//非同期処理を行う関数を定義
  const response = await fetch(apiBaseUrl);
  //fetchでapiBaseUrlのURLにGETリクエストを送信して返ってきたデータ全体をresponseに代入
  const users = await response.json();
  //responseのデータをjson形式に変換してusersに代入

  const userList = document.getElementById('user-list');
  //HTMLのIDがuser-listの要素を取得してuserListに代入
  userList.innerHTML = ''; 
  // リストを初期化

  users.forEach(user => {
  //forEachメソッドでusersの中身を一つずつ取り出し、userに代入
    const li = document.createElement('li');
    //liタグを作成してliに代入
    li.textContent = `${user.name} (${user.email})`;
    //作成したliタグの中に取得したuserのnameとemailを表示
    const editLink = document.createElement('a');
    //aタグを作成してeditLinkに代入
    editLink.href = `edit.html?id=${user.id}`;
    // editLinkにhref属性を付与し、URLクエリパラメーターとしてユーザーIDを設定
    editLink.textContent = 'Edit';
    //editlinkをEditとして設定
    li.appendChild(editLink);
    //li要素の一番最後にeditlinkを子要素として追加
    userList.appendChild(li);
    //userListの一番最後にliタグを子要素として追加
  });
}

// 新しいユーザーを追加
async function addUser(event) {
//addUser関数にevent引数を持たせる
  event.preventDefault();
  //ページのリロードを防ぐ
  const name = document.getElementById('user-name').value;
  //HTMLのIDがuser-nameの要素に入力されたvalue(値)をnameに代入
  const email = document.getElementById('user-email').value;
  //HTMLのIDがuser-emailの要素に入力されたvalue(値)をemailに代入
  const response = await fetch(apiBaseUrl, {
    method: 'POST',
    //POSTリクエストを指定
    headers: {
      'Content-Type': 'application/json',
    },
    //サーバーに送信するデータがjsonだと通知
    body: JSON.stringify({ name, email }),
    //サーバーにデータの中身を設定
  });
    
  if (response.ok) {
  //fetchで送ったHTTPレスポンスが200番台（成功）だった場合
    document.getElementById('add-user-form').reset();
    //HTMLのIDがadd-user-nameのフォーム要素をリセット（初期状態に戻す）
    fetchUsers(); 
    // 上記で定義したfetchUsers関数を呼び出し
  } else {
  //成功しなかった場合（成功以外）
    const error = await response.json();
    //失敗した場合は、サーバーから失敗の内容をjson形式に変換してerrorに代入
    alert(`Error: ${error.errors.join(', ')}`);
    //アラートダイアログでeroorの中のerrorsプロパティをカンマで繋げて出力
  }
}

// イベントリスナーを設定
document.getElementById('add-user-form').addEventListener('submit', addUser);

// ページ初期化時にユーザー一覧を取得
fetchUsers();
```
 今回script.jsにはuserの一覧取得と新規作成の記述しかしておりません（長くなるため）
## まとめ
 このように、scriptの関数の中でrailsサーバーと通信してデータのやり取りをすることができます。
これまではrailsの中にjavascriptを埋め込んで操作したりを学んできましたが、開発現場によっては外部のファイルにrails APIを連携して使えるということを学びました。