T-CREATOR

Remix Loader/Action チートシート:Request/Response API 逆引き大全

Remix Loader/Action チートシート:Request/Response API 逆引き大全

Remix で Web アプリケーションを開発していると、Loader や Action で Request を処理し、Response を返す場面が頻繁に訪れます。しかし、「URL パラメータはどう取得するんだっけ?」「Cookie の設定方法は?」と、毎回調べ直していませんか?

この記事では、Remix の Loader と Action で使える Request/Response API を逆引き形式でまとめました。実務で「あ、これどうやるんだっけ?」と思った瞬間に、すぐ答えが見つかるチートシートとして活用していただけます。

Loader/Action Request API 早見表

開発中に「このデータはどう取得するの?」と迷ったら、この表をご覧ください。

#やりたいことAPI使用例
1URL パラメータを取得new URL(request.url).searchParams?page=2searchParams.get("page")
2Form データを取得await request.formData()formData.get("username")
3JSON ボディを取得await request.json()const data = await request.json()
4リクエストヘッダーを取得request.headers.get()request.headers.get("Authorization")
5Cookie を取得request.headers.get("Cookie")cookie.parse(request.headers.get("Cookie"))
6リクエストメソッドを確認request.methodif (request.method === "POST")
7パスパラメータを取得params 引数loader({ params }) => params.id
8リクエスト URL 全体を取得request.urlconst url = new URL(request.url)

Loader/Action Response API 早見表

「このレスポンスはどう返すの?」と悩んだら、この表で即座に解決できます。

#やりたいことAPI使用例
1JSON レスポンスを返すjson()return json({ message: "成功" })
2リダイレクトするredirect()return redirect("​/​dashboard")
3ステータスコードを指定json(data, { status })return json({ error: "Not Found" }, { status: 404 })
4カスタムヘッダーを付与json(data, { headers })return json(data, { headers: { "X-Custom": "value" } })
5Cookie を設定json(data, { headers: { "Set-Cookie" } })headers: { "Set-Cookie": await commitSession(session) }
6フォームエラーを返すjson({ errors })return json({ errors: { email: "無効です" } })
7複数の Cookie を設定複数の Set-Cookie ヘッダーheaders.append("Set-Cookie", cookie1)
8ストリーミングレスポンスnew Response(stream)return new Response(stream, { headers })

背景

Remix は、React ベースのフルスタックフレームワークとして、サーバーサイドとクライアントサイドをシームレスに統合する設計になっています。この設計の中核を担うのが LoaderAction です。

Loader はページのデータ取得を担当し、Action はフォーム送信などのデータ変更処理を担当します。どちらも Web 標準の Request オブジェクトを受け取り、Response オブジェクトを返すという、シンプルかつ強力な仕組みです。

しかし、このシンプルさゆえに「どの API をどう使えばいいのか」が散らばっており、毎回調べ直すことになりがちです。特に以下のような場面で迷いが生じます。

Remix における Loader と Action の基本的なデータフローを図で確認しましょう。

mermaidflowchart LR
  client["クライアント<br/>ブラウザ"] -->|GET リクエスト| loader["Loader"]
  client -->|POST/PUT/DELETE| action["Action"]
  loader -->|データ取得| db[("データベース")]
  action -->|データ更新| db
  loader -->|json 応答| client
  action -->|redirect または json| client

この図が示すように、Loader はデータ取得、Action はデータ変更を担当し、それぞれが Request を処理して Response を返します。

課題

Remix の Loader と Action を使いこなすには、以下のような課題があります。

API が散在している

URL パラメータの取得には new URL(request.url).searchParams を使い、Form データには request.formData() を使い、Cookie には request.headers.get("Cookie") を使うなど、API が散在しています。どの場面でどの API を使うべきか、記憶に頼るのは効率的ではありません。

Web 標準 API との違いが不明瞭

Remix は Web 標準の Request/Response API をベースにしていますが、Remix 独自のヘルパー関数(json()redirect() など)も用意されています。「標準 API とヘルパーのどちらを使うべきか」が初見では分かりにくいのです。

エラーハンドリングのベストプラクティスが分散

フォームのバリデーションエラーを返す場合、Action でどのようなレスポンスを返せばいいのか。ステータスコードは 400 にすべきか、200 のまま errors フィールドを返すべきか。こうした判断基準が分散しています。

型安全性の確保が難しい

TypeScript を使っていても、formData.get() の戻り値は FormDataEntryValue | null 型であり、実際の型が不明瞭です。型アサーションを多用することになり、型安全性が損なわれがちです。

これらの課題を解決するには、実務でよく使うパターンを逆引き形式で整理し、すぐに参照できる形にまとめることが有効です。

Loader と Action が直面する課題を図で整理すると、以下のようになります。

mermaidflowchart TD
  request["Request オブジェクト"] --> question1{"何を取得する?"}
  question1 -->|URL パラメータ| api1["searchParams"]
  question1 -->|Form データ| api2["formData"]
  question1 -->|JSON| api3["json"]
  question1 -->|Headers| api4["headers.get"]

  response["Response 生成"] --> question2{"何を返す?"}
  question2 -->|データ| json_helper["json ヘルパー"]
  question2 -->|画面遷移| redirect_helper["redirect ヘルパー"]
  question2 -->|エラー| error_response["json + status 400"]

解決策

これらの課題を解決するため、この記事では Remix の Loader/Action で頻出する Request/Response API を逆引き形式で整理します。

Request API の体系化

Request から情報を取得する API を、以下のカテゴリに分類します。

  1. URL 情報の取得: パラメータ、パス、クエリ文字列
  2. ボディ情報の取得: Form データ、JSON、テキスト
  3. メタ情報の取得: Headers、Cookies、メソッド

それぞれのカテゴリで、どの API をどう使えばいいのかを明示します。

Response API の体系化

Response を返す API を、以下のカテゴリに分類します。

  1. データレスポンス: json() ヘルパー、カスタムヘッダー、ステータスコード
  2. リダイレクトレスポンス: redirect() ヘルパー、外部リダイレクト
  3. エラーレスポンス: フォームエラー、API エラー、バリデーションエラー

これにより、「○○ をしたい」という目的から API を逆引きできるようになります。

実践的なコード例の提示

すべての API に対して、実際に動作するコード例を提示します。コピー&ペーストで即座に使える形にすることで、開発速度を向上させます。

TypeScript による型安全性の向上

formData.get() の戻り値を型安全に扱う方法、Zod などのバリデーションライブラリとの組み合わせ方など、型安全性を確保するパターンも紹介します。

具体例

ここからは、実際のコード例を交えながら、Request/Response API の使い方を解説していきます。

Request API の使い方

URL パラメータの取得

URL のクエリパラメータ(?page=2&sort=name など)を取得する方法です。

Remix の Loader では、request.url から URL オブジェクトを生成し、searchParams プロパティでクエリパラメータにアクセスできます。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';

export async function loader({
  request,
}: LoaderFunctionArgs) {
  // URL オブジェクトを生成
  const url = new URL(request.url);

  // クエリパラメータを取得
  const page = url.searchParams.get('page') || '1';
  const sort = url.searchParams.get('sort') || 'name';

  return { page, sort };
}

searchParams.get() は、パラメータが存在しない場合に null を返すため、デフォルト値を設定するのが一般的です。

複数の値を持つクエリパラメータ(?tags=react&tags=remix)を取得する場合は、getAll() を使います。

typescriptexport async function loader({
  request,
}: LoaderFunctionArgs) {
  const url = new URL(request.url);

  // 複数の値を配列で取得
  const tags = url.searchParams.getAll('tags');
  // tags = ["react", "remix"]

  return { tags };
}

パスパラメータの取得

動的ルート(routes​/​posts.$id.tsx など)のパラメータを取得する方法です。

Remix では、Loader や Action の第一引数に params オブジェクトが渡されます。ここからパスパラメータを取得できます。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  params,
}: LoaderFunctionArgs) {
  // パスパラメータを取得
  const postId = params.id;

  // postId を使ってデータを取得
  const post = await getPost(postId);

  return json({ post });
}

パスパラメータは必ず存在するため、null チェックは不要ですが、TypeScript で厳密に型を付ける場合は params.id! とするか、バリデーションを行うと安全です。

Form データの取得

POST リクエストで送信されたフォームデータを取得する方法です。

Action では、request.formData() を使ってフォームデータを取得します。これは非同期メソッドなので、await が必要です。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json, redirect } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  // フォームデータを取得
  const formData = await request.formData();

  // 各フィールドの値を取得
  const username = formData.get('username');
  const email = formData.get('email');

  return json({ username, email });
}

formData.get() の戻り値は FormDataEntryValue | null 型(つまり string | File | null)なので、型安全に扱うにはバリデーションが必要です。

フォームデータのバリデーションを型安全に行う例を見てみましょう。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();

  // 文字列として取得し、バリデーション
  const username = formData.get('username');
  const email = formData.get('email');

  // バリデーションエラーを収集
  const errors: Record<string, string> = {};

  if (!username || typeof username !== 'string') {
    errors.username = 'ユーザー名は必須です';
  }

  if (
    !email ||
    typeof email !== 'string' ||
    !email.includes('@')
  ) {
    errors.email = '有効なメールアドレスを入力してください';
  }

  // エラーがあれば返す
  if (Object.keys(errors).length > 0) {
    return json({ errors }, { status: 400 });
  }

  // 処理を続行
  return json({ success: true });
}

JSON ボディの取得

API エンドポイントとして使う場合、JSON ボディを取得する方法です。

request.json() を使うことで、JSON 形式のリクエストボディをパースできます。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  // JSON ボディを取得
  const data = await request.json();

  // TypeScript の型アサーション
  const { title, content } = data as {
    title: string;
    content: string;
  };

  return json({ title, content });
}

実務では、Zod などのスキーマバリデーションライブラリを使うことで、型安全性を確保できます。

typescriptimport { z } from 'zod';
import { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

// スキーマを定義
const PostSchema = z.object({
  title: z.string().min(1, 'タイトルは必須です'),
  content: z.string().min(10, '本文は10文字以上必要です'),
});

export async function action({
  request,
}: ActionFunctionArgs) {
  const data = await request.json();

  // バリデーション
  const result = PostSchema.safeParse(data);

  if (!result.success) {
    return json(
      { errors: result.error.flatten() },
      { status: 400 }
    );
  }

  // 型安全なデータ
  const { title, content } = result.data;

  return json({ title, content });
}

Headers の取得

リクエストヘッダー(Authorization、User-Agent など)を取得する方法です。

request.headers.get() を使うことで、任意のヘッダーを取得できます。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  request,
}: LoaderFunctionArgs) {
  // Authorization ヘッダーを取得
  const authHeader = request.headers.get('Authorization');

  if (!authHeader) {
    return json(
      { error: '認証が必要です' },
      { status: 401 }
    );
  }

  // Bearer トークンを抽出
  const token = authHeader.replace('Bearer ', '');

  return json({ token });
}

ヘッダーは大文字小文字を区別しないため、Authorization でも authorization でも同じ値を取得できます。

Cookie からセッション情報などを取得する方法です。

Cookie は Cookie ヘッダーに格納されているため、request.headers.get("Cookie") で取得し、パースする必要があります。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  request,
}: LoaderFunctionArgs) {
  // Cookie ヘッダーを取得
  const cookieHeader = request.headers.get('Cookie');

  // cookie パーサーを使用(例: cookie パッケージ)
  const cookies = cookieHeader
    ? Object.fromEntries(
        cookieHeader.split('; ').map((c) => c.split('='))
      )
    : {};

  const sessionId = cookies.sessionId;

  return json({ sessionId });
}

実務では、Remix の createCookiecreateCookieSessionStorage を使うことで、より安全に Cookie を扱えます。

typescriptimport { createCookie } from '@remix-run/node';
import { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

// Cookie を定義
const userPrefs = createCookie('user-prefs', {
  maxAge: 60 * 60 * 24 * 365, // 1年
});

export async function loader({
  request,
}: LoaderFunctionArgs) {
  // Cookie を取得してパース
  const prefs = await userPrefs.parse(
    request.headers.get('Cookie')
  );

  return json({ theme: prefs?.theme || 'light' });
}

リクエストメソッドの確認

GET、POST、PUT、DELETE などのメソッドを確認する方法です。

Action は通常 POST メソッドで呼ばれますが、PUT や DELETE にも対応できます。request.method でメソッドを確認できます。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  // リクエストメソッドを確認
  const method = request.method;

  switch (method) {
    case 'POST':
      // 新規作成
      return json({ action: 'create' });

    case 'PUT':
      // 更新
      return json({ action: 'update' });

    case 'DELETE':
      // 削除
      return json({ action: 'delete' });

    default:
      return json(
        { error: 'サポートされていないメソッドです' },
        { status: 405 }
      );
  }
}

Form から method="POST" 以外のメソッドを送信したい場合は、hidden input で _method フィールドを使うのが一般的です。

Response API の使い方

JSON レスポンスを返す

最も基本的な JSON レスポンスの返し方です。

Remix の json() ヘルパーを使うことで、簡潔に JSON レスポンスを返せます。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const data = {
    message: '成功しました',
    timestamp: Date.now(),
  };

  // JSON レスポンスを返す
  return json(data);
}

json() は内部で new Response(JSON.stringify(data), { headers: { "Content-Type": "application​/​json" } }) を実行しているため、手動で Response を作成する必要がありません。

ステータスコードを指定する

エラー時に 404 や 500 などのステータスコードを返す方法です。

json() の第二引数に { status } を渡すことで、ステータスコードを指定できます。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  params,
}: LoaderFunctionArgs) {
  const post = await getPost(params.id);

  // データが見つからない場合
  if (!post) {
    return json(
      { error: '投稿が見つかりませんでした' },
      { status: 404 }
    );
  }

  return json({ post });
}

ステータスコード 404 を返すことで、Remix の ErrorBoundary が自動的に呼ばれます。

バリデーションエラーを返す場合は、ステータスコード 400 を使います。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const email = formData.get('email');

  // バリデーションエラー
  if (
    !email ||
    typeof email !== 'string' ||
    !email.includes('@')
  ) {
    return json(
      {
        errors: {
          email: '有効なメールアドレスを入力してください',
        },
      },
      { status: 400 }
    );
  }

  return json({ success: true });
}

カスタムヘッダーを付与する

キャッシュ制御や CORS ヘッダーなど、カスタムヘッダーを付与する方法です。

json() の第二引数に { headers } を渡すことで、カスタムヘッダーを追加できます。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const data = { message: 'キャッシュ可能なデータ' };

  // カスタムヘッダーを付与
  return json(data, {
    headers: {
      'Cache-Control': 'public, max-age=3600',
      'X-Custom-Header': 'MyValue',
    },
  });
}

CORS を有効にする場合も、同様にヘッダーを追加します。

typescriptimport { type LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const data = { message: 'CORS が有効です' };

  return json(data, {
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods':
        'GET, POST, PUT, DELETE',
      'Access-Control-Allow-Headers':
        'Content-Type, Authorization',
    },
  });
}

レスポンスに Cookie を設定する方法です。

Set-Cookie ヘッダーを使うことで、Cookie を設定できます。Remix の createCookie ヘルパーを使うと、より安全に Cookie を設定できます。

typescriptimport { createCookie } from '@remix-run/node';
import { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

// Cookie を定義
const themeCookie = createCookie('theme', {
  maxAge: 60 * 60 * 24 * 365, // 1年
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
});

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const theme = formData.get('theme') as string;

  // Cookie を設定
  return json(
    { success: true },
    {
      headers: {
        'Set-Cookie': await themeCookie.serialize(theme),
      },
    }
  );
}

複数の Cookie を設定する場合は、Headers オブジェクトを使います。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  const headers = new Headers();

  // 複数の Cookie を追加
  headers.append(
    'Set-Cookie',
    'cookie1=value1; Path=/; HttpOnly'
  );
  headers.append(
    'Set-Cookie',
    'cookie2=value2; Path=/; HttpOnly'
  );

  return json({ success: true }, { headers });
}

リダイレクトする

ログイン後のダッシュボードへの遷移など、リダイレクトする方法です。

Remix の redirect() ヘルパーを使うことで、簡潔にリダイレクトできます。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { redirect } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const username = formData.get('username') as string;

  // ユーザーを作成
  await createUser(username);

  // ダッシュボードにリダイレクト
  return redirect('/dashboard');
}

デフォルトでは 302 リダイレクトが返されますが、ステータスコードを変更することもできます。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { redirect } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  await deleteUser();

  // 301 永続的なリダイレクト
  return redirect('/', { status: 301 });
}

リダイレクト時に Cookie を設定する場合は、headers オプションを使います。

typescriptimport { createCookieSessionStorage } from '@remix-run/node';
import { type ActionFunctionArgs } from '@remix-run/node';
import { redirect } from '@remix-run/node';

const { getSession, commitSession } =
  createCookieSessionStorage({
    cookie: {
      name: '__session',
      httpOnly: true,
      secure: true,
    },
  });

export async function action({
  request,
}: ActionFunctionArgs) {
  const session = await getSession(
    request.headers.get('Cookie')
  );

  // セッションにデータを設定
  session.set('userId', '12345');

  // Cookie を設定してリダイレクト
  return redirect('/dashboard', {
    headers: {
      'Set-Cookie': await commitSession(session),
    },
  });
}

フォームエラーを返す

フォームのバリデーションエラーをクライアントに返す方法です。

Remix では、Action から json({ errors }) を返すことで、コンポーネント側で useActionData() を使ってエラーを取得できます。

typescriptimport { type ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const email = formData.get('email') as string;
  const password = formData.get('password') as string;

  const errors: Record<string, string> = {};

  // バリデーション
  if (!email || !email.includes('@')) {
    errors.email = '有効なメールアドレスを入力してください';
  }

  if (!password || password.length < 8) {
    errors.password = 'パスワードは8文字以上必要です';
  }

  // エラーがあれば返す
  if (Object.keys(errors).length > 0) {
    return json({ errors }, { status: 400 });
  }

  // 成功時の処理
  return redirect('/dashboard');
}

クライアント側では、useActionData() を使ってエラーを表示します。

typescriptimport { useActionData } from '@remix-run/react';

export default function LoginPage() {
  const actionData = useActionData<typeof action>();

  return (
    <form method='post'>
      <div>
        <label>メールアドレス</label>
        <input type='email' name='email' />
        {actionData?.errors?.email && (
          <p style={{ color: 'red' }}>
            {actionData.errors.email}
          </p>
        )}
      </div>

      <div>
        <label>パスワード</label>
        <input type='password' name='password' />
        {actionData?.errors?.password && (
          <p style={{ color: 'red' }}>
            {actionData.errors.password}
          </p>
        )}
      </div>

      <button type='submit'>ログイン</button>
    </form>
  );
}

実践的な統合例

Loader と Action を組み合わせた、実践的な CRUD 操作の例を見てみましょう。

以下は、ブログ投稿の編集ページの例です。Loader でデータを取得し、Action で更新を処理します。

typescriptimport {
  type LoaderFunctionArgs,
  type ActionFunctionArgs,
} from '@remix-run/node';
import { json, redirect } from '@remix-run/node';
import {
  useLoaderData,
  useActionData,
  Form,
} from '@remix-run/react';

// Loader: 編集対象の投稿を取得
export async function loader({
  params,
}: LoaderFunctionArgs) {
  const post = await getPost(params.id);

  if (!post) {
    return json(
      { error: '投稿が見つかりませんでした' },
      { status: 404 }
    );
  }

  return json({ post });
}

// Action: 投稿を更新
export async function action({
  request,
  params,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const title = formData.get('title') as string;
  const content = formData.get('content') as string;

  // バリデーション
  const errors: Record<string, string> = {};

  if (!title || title.length < 3) {
    errors.title = 'タイトルは3文字以上必要です';
  }

  if (!content || content.length < 10) {
    errors.content = '本文は10文字以上必要です';
  }

  if (Object.keys(errors).length > 0) {
    return json({ errors }, { status: 400 });
  }

  // 更新処理
  await updatePost(params.id, { title, content });

  // 詳細ページにリダイレクト
  return redirect(`/posts/${params.id}`);
}

コンポーネント側では、Loader のデータと Action のエラーを組み合わせて表示します。

typescriptexport default function EditPost() {
  const { post } = useLoaderData<typeof loader>();
  const actionData = useActionData<typeof action>();

  return (
    <div>
      <h1>投稿を編集</h1>

      <Form method='post'>
        <div>
          <label>タイトル</label>
          <input
            type='text'
            name='title'
            defaultValue={post.title}
          />
          {actionData?.errors?.title && (
            <p style={{ color: 'red' }}>
              {actionData.errors.title}
            </p>
          )}
        </div>

        <div>
          <label>本文</label>
          <textarea
            name='content'
            defaultValue={post.content}
            rows={10}
          />
          {actionData?.errors?.content && (
            <p style={{ color: 'red' }}>
              {actionData.errors.content}
            </p>
          )}
        </div>

        <button type='submit'>更新</button>
      </Form>
    </div>
  );
}

この例では、Loader でデータを取得し、Action でバリデーションと更新を行い、エラーがあればフォームに表示し、成功すればリダイレクトするという、典型的な CRUD パターンを実装しています。

Request と Response の処理フローを図で確認しましょう。

mermaidsequenceDiagram
  participant User as ユーザー
  participant Remix as Remix アプリ
  participant Loader as Loader 関数
  participant Action as Action 関数
  participant DB as データベース

  User->>Remix: GET /posts/123
  Remix->>Loader: loader({ params, request })
  Loader->>DB: データ取得
  DB-->>Loader: Post データ
  Loader-->>Remix: json({ post })
  Remix-->>User: HTML + データ

  User->>Remix: POST /posts/123 (Form 送信)
  Remix->>Action: action({ params, request })
  Action->>Action: formData 取得
  Action->>Action: バリデーション
  alt エラーあり
    Action-->>Remix: json({ errors }, 400)
    Remix-->>User: エラー表示
  else 成功
    Action->>DB: データ更新
    DB-->>Action: 完了
    Action-->>Remix: redirect("/posts/123")
    Remix-->>User: リダイレクト
  end

まとめ

この記事では、Remix の Loader と Action で使える Request/Response API を逆引き形式でまとめました。

Request API では、以下の取得方法を解説しました。

  • URL パラメータ: new URL(request.url).searchParams
  • パスパラメータ: params オブジェクト
  • Form データ: await request.formData()
  • JSON ボディ: await request.json()
  • Headers: request.headers.get()
  • Cookie: request.headers.get("Cookie") + パース
  • リクエストメソッド: request.method

Response API では、以下の返し方を解説しました。

  • JSON レスポンス: json(data)
  • ステータスコード指定: json(data, { status })
  • カスタムヘッダー: json(data, { headers })
  • Cookie 設定: Set-Cookie ヘッダー
  • リダイレクト: redirect(url)
  • フォームエラー: json({ errors }, { status: 400 })

これらの API を組み合わせることで、Remix で堅牢な Web アプリケーションを開発できます。開発中に「あれ、これどうやるんだっけ?」と思ったら、ぜひこの記事を参照してください。

早見表を印刷して手元に置いておくと、さらに開発効率が向上するでしょう。Remix の Loader と Action を使いこなして、快適な開発体験を手に入れてください。

関連リンク