mcpField
I
inertia-rails-controllers

inertia-rails-controllers

github.com/inertia-rails開発70493 回インストール37 週間前に更新
リポジトリ

inertia-rails-controllers について

RailsコントローラーとInertia.jsを使うエンジニア向けのサーバー側パターンガイドです。ページプロパティ、共有データ、遅延・任意の値、バリデーションエラー、外部リダイレクトを解説します。

主な機能

  1. 01コントローラーの描画とプロパティ渡しのルール
  2. 02共有データとフラッシュ設定
  3. 03遅延・任意・キャッシュプロパティ
  4. 04バリデーションエラーのフォーム項目への対応
  5. 05外部リダイレクトと履歴の暗号化

活用シーン

  1. 01Inertia.jsページへのコントローラーデータの受け渡し
  2. 02遅いクエリや部分更新用プロパティの処理
  3. 03バリデーションエラーと入力項目の対応付け
  4. 04決済またはOAuthサービスへのリダイレクト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/inertia-rails/skills --skill inertia-rails-controllers

インストール後、Claude Code で/inertia-rails-controllersを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
authorization.md
configuration.md
prop-types.md

詳細

Inertia Rails コントローラー

Inertia のレスポンスを返す Rails コントローラーのサーバーサイドパターンです。

プロパティを追加する前に確認してください:

  • すべてのページで必要ですか? → アクションごとのプロパティではなく、ベースコントローラー(InertiaController)の inertia_share
  • 計算コストが高いですか? → InertiaRails.defer — ページをすばやく読み込み、後からデータをストリーミングします
  • 部分的な再読み込みでのみ必要ですか? → InertiaRails.optional — 初回読み込みではスキップされます
  • めったに変わらない参照データですか? → InertiaRails.once — ページ遷移をまたいでキャッシュされます

絶対にしないでください:

  • 外部 URL(Stripe、OAuth、SSO)へのリダイレクトに redirect_to を使う — 302 を返しますが、Inertia クライアントはレスポンスを JSON として解析しようとするため、リダイレクトが壊れます。inertia_location を使ってください(409 と X-Inertia-Location ヘッダーを返します)。
  • バリデーションエラーに errors.full_messages を使う — フィールドキーのない単純な文字列が生成されるため、フロントエンドでエラーを対応する入力フィールドにマッピングできません。errors.to_hash(true) を使ってください。
  • inertia.defer、Inertia.defer、または inertia_rails.defer を使う — 正しい構文は InertiaRails.defer { ... } です。すべてのプロパティヘルパーは InertiaRails 定数上のモジュールメソッドです。
  • インスタンス変数が自動的にプロパティとして渡されると想定する — 自動では渡されません(alba-inertia gem が設定されている場合を除く)。フロントエンドにプロパティを渡すすべてのアクションで、render inertia: { key: data } を呼び出す必要があります。
  • config.flash_keys を更新せずに success/error をフラッシュキーとして使う — Rails のデフォルトは notice/alert です。カスタムキーはイニシャライザーの設定と TypeScript の FlashData 型の両方に追加する必要があります。

レンダリング構文

default_render: true の落とし穴: この設定はコントローラー/アクションからコンポーネント名を自動推測するだけで、インスタンス変数をプロパティとして自動的に渡すわけではありません。default_render: true を設定したアクションで @posts = Post.all と記述すると、正しいコンポーネントはレンダリングされますが、フロントエンドにデータは一切送られません。 インスタンス変数がプロパティとして自動シリアライズされるのは、alba-inertia gem が設定されている場合のみです。これに依存する前に Gemfile を確認してください。この gem がなければ、ページにデータを渡すには render inertia: { posts: data } を使う必要があります。

空のアクション(def index; end)が正しいのは、データを必要としないページ(例:静的なダッシュボードページ、ログインフォーム)のみです。アクションがデータベースをクエリする場合は、データとともに render inertia: を呼び出す必要があります。

状況構文コンポーネントのパス
アクションがデータを読み込むrender inertia: { users: data }コントローラー/アクションから推測
アクションがデータを読み込まない(静的ページ)空のアクション、または render inertia: {}コントローラー/アクションから推測
別のページをレンダリングするrender inertia: 'errors/show', props: { error: e }明示的なパス

目安: アクションがデータベースにアクセスする場合は、データとともに render inertia: を呼び出す必要があります。 アクション本体が空の場合、ページに渡されるのは共有プロパティ(inertia_share のもの)のみです。

# CORRECT — data passed as props
def index
  render inertia: { users: users_data, stats: InertiaRails.defer { ExpensiveQuery.run } }
end

# CORRECT — static page, no data needed
def index; end

# WRONG — @posts is NEVER sent to the frontend (without alba-inertia)
def index
  @posts = Post.all
end

注: プロジェクトで alba-inertia gem を使っている場合(Gemfile を確認してください)、インスタンス変数はプロパティとして自動シリアライズされるため、明示的な render inertia: は不要です。 この規約については alba-inertia スキルを参照してください。

Prop Types

InertiaRails.defer — inertia.deferでもInertia.deferでもありません。すべてのプロップヘルパーはInertiaRailsのモジュールメソッドです。

タイプ構文動作
通常{ key: value }常に評価され、常に含まれます
遅延-> { expensive_value }初回ページのレンダリング時に含まれ、部分リロード時には遅延評価されます
オプションInertiaRails.optional { ... }リクエストで指定された場合にのみ評価されます
DeferInertiaRails.defer { ... }初回ページのレンダリング後に読み込まれます
Defer(グループ化)InertiaRails.defer(group: 'name') { ... }グループ化された遅延データ — 並列で取得されます
OnceInertiaRails.once { ... }一度だけ解決され、ナビゲーション間で保持されます
MergeInertiaRails.merge { ... }既存の配列に追加されます(無限スクロール)
Deep mergeInertiaRails.deep_merge { ... }既存のオブジェクトにディープマージされます
AlwaysInertiaRails.always { ... }部分リロード時も含まれます
ScrollInertiaRails.scroll { ... }無限スクロール用のスクロール対応プロップです
def index
  render inertia: {
    filters: filter_params,
    messages: -> { messages_scope.as_json },
    stats: InertiaRails.defer { Dashboard.stats },
    chart: InertiaRails.defer(group: 'analytics') { Dashboard.chart },
    countries: InertiaRails.once { Country.pluck(:name, :code) },
    posts: InertiaRails.merge { @posts.as_json },
    csrf: InertiaRails.always { form_authenticity_token },
  }
end
遅延プロップ — フルスタックの例

サーバーは時間のかかるデータを遅延させ、クライアントはフォールバックを表示した後、コンテンツに差し替えます。

# Controller
def show
  render inertia: {
    basic_stats: Stats.quick_summary,
    analytics: InertiaRails.defer { Analytics.compute_slow },
  }
end
// Page component — child reads deferred prop from page props
import { Deferred, usePage } from '@inertiajs/react'

export default function Dashboard({ basic_stats }: Props) {
  return (
    <>
      <QuickStats data={basic_stats} />
      <Deferred data="analytics" fallback={<div>Loading analytics...</div>}>
        <AnalyticsPanel />
      </Deferred>
    </>
  )
}

function AnalyticsPanel() {
  const { analytics } = usePage<{ analytics: Analytics }>().props
  return <div>{analytics.revenue}</div>
}

共有データ

コントローラーではinertia_shareを使用します。コントローラーのコンテキスト(current_user、 リクエスト)が必要です。イニシャライザーで扱うのはconfig.*の設定(version、flash_keys)のみです。

class ApplicationController < ActionController::Base
  # Static
  inertia_share app_name: 'MyApp'

  # Using lambdas (most common)
  inertia_share auth: -> { { user: current_user&.as_json(only: [:id, :name, :email, :role]) } }

  # Conditional
  inertia_share if: :user_signed_in? do
    { notifications: -> { current_user.unread_notifications_count } }
  end
end

ラムダ版とアクションスコープ版については、references/configuration.mdを参照してください。

評価順序: 複数のinertia_share呼び出しは上から順にマージされます。子コントローラーが親と同じキーを共有する場合は、子の値が優先されます。ブロックとラムダによる共有はリクエストごとに遅延評価され、Inertia以外のリクエストでは実行されません。

フラッシュメッセージ

フラッシュは自動です。必要に応じて公開するキーを設定してください。

# config/initializers/inertia_rails.rb
InertiaRails.configure do |config|
  config.flash_keys = %i[notice alert toast] # default: %i[notice alert]
end

コントローラーでは標準のRailsフラッシュを使用します。

redirect_to users_path, notice: "User created!"
# or
flash.alert = "Something went wrong"
redirect_to users_path

リダイレクトとバリデーションエラー

作成・更新・削除の後は、必ずリダイレクトしてください(Post-Redirect-Get)。標準のRailsのredirect_toが使用できます。Inertia固有の対応が必要なのは、バリデーションエラーの処理です。

def create
  @user = User.new(user_params)
  if @user.save
    redirect_to users_path, notice: "Created!"
  else
    redirect_back_or_to new_user_path, inertia: { errors: @user.errors.to_hash(true) }
  end
end

to_hashとto_hash(true)の違い: to_hashは{ name: ["can't be blank"] }を返し、to_hash(true)は{ name: ["Name can't be blank"] }を返します。キーは入力欄のname属性と一致している必要があります。一致しない場合、エラーは対応する入力欄の横に表示されません。

errors.full_messagesは絶対に使用しないでください — フィールドキーのないフラットな文字列が生成されるため、フロントエンドでエラーを対応する入力欄に紐付けられません。

プロップとしての認可情報

リソースごとのcanハッシュとして権限を渡してください。フロントエンドは表示を制御し、サーバーはアクセスを強制します。inertia-rails-controllersとinertia-rails-pagesのスキルを参照してください。

必須 — 認可プロップを実装する場合はファイル全体を読むこと: references/authorization.md(約40行) — Action Policy/Pundit/CanCanCanの例を含む、フルスタックのcanパターンです。

フロントエンドに権限データを渡さない場合は読み込まないでください。

外部リダイレクト(inertia_location)

重要: 外部URLへのredirect_toはInertiaを壊します。クライアントは302を受け取りますが、ページ全体のリダイレクトではなく、Inertiaのレスポンス(JSON)として処理しようとします。inertia_locationはX-Inertia-Locationヘッダー付きの409を返し、クライアントにwindow.location = urlを実行させます。

# Stripe checkout — MUST use inertia_location, not redirect_to
def create
  checkout_session = Current.user.payment_processor.checkout(
    mode: "payment",
    line_items: "price_xxx",
    success_url: enrollments_url,
    cancel_url: course_url(@course),
  )
  inertia_location checkout_session.url
end

決済プロバイダー、OAuth、外部サービスなど、Inertiaアプリの外部にあるURLにはinertia_locationを使用してください。

履歴の暗号化

ブラウザーの履歴状態に含まれるページデータを暗号化します — config.encrypt_history = Rails.env.production?。 ログアウトやロール変更時にはredirect_to path, inertia: { clear_history: true }を使用してください。 サーバー側とクライアント側の例を含むセットアップ全体については、 references/configuration.mdを参照してください。

設定

InertiaRails.configureのすべてのオプション(version、encrypt_history、flash_keysなど)については、references/configuration.mdを参照してください。

トラブルシューティング

症状原因対処方法
Stripe/OAuth リダイレクトで 302 ループが発生する外部 URL に対して redirect_to を使用しているinertia_location を使用してください。409 と X-Inertia-Location ヘッダーが返されます
エラーがフィールドの横に表示されないエラーキーが入力の name と一致していないto_hash のキーは入力の name 属性と完全に一致させる必要があります
TS2305:@/routes に postsPath が見つからないルート追加後に js-routes が再生成されていないconfig/routes.rb を変更した後、rails js_routes:generate を実行してください

関連スキル

  • フォームエラーの表示 → inertia-rails-forms
  • フラッシュトースト UI → inertia-rails-pages(アクセス)+ shadcn-inertia(Sonner)
  • クライアント側での遅延表示 → inertia-rails-pages(<Deferred> コンポーネント)
  • 型安全な props → inertia-rails-typescript または alba-inertia(シリアライザー)
  • テスト → inertia-rails-testing

参考資料

merge、scroll、deep_merge などの高度な prop 型を使用する場合、または複数の prop オプションを組み合わせる場合は、必ずファイル全体を読んでください: references/prop-types.md(約 180 行) — すべての prop 型の詳細な動作、エッジケース、組み合わせルール。

基本的な defer、optional、once、always の使用では、prop-types.md を読み込まないでください。上の表で十分です。

初めて InertiaRails.configure を設定するとき、または設定に関する問題をデバッグするときに限り、references/configuration.md(約 180 行)を読み込んでください。通常のコントローラー作業では読み込まないでください。

コメント