コンテンツにスキップ

APIルート

サーバー上で実行されない、あるいはほとんど実行されないReactアプリケーションを構築することに慣れているかもしれません。そのような場合、アプリケーションはAPIルートのセットによってサポートされています。Remixでは、ほとんどのルートがUIとAPIの両方を兼ねており、ブラウザ上のRemixはサーバー上の自身と通信する方法を知っています。

一般的に、「APIルート」という概念は全く必要ありません。しかし、この用語に興味を持つことは分かっているので、ここで説明します!

ルートは独自のAPIを持つ

以下のルートを考えてみましょう:

app/routes/teams.tsx
export async function loader() {
return json(await getTeams());
}
export default function Teams() {
return (
<TeamsView teams={useLoaderData<typeof loader>()} />
);
}

ユーザーが<Link to="/teams" />へのリンクをクリックするたびに、ブラウザ上のRemixはサーバーへのフェッチを実行し、loaderからデータを取得してルートをレンダリングします。コンポーネントへのデータ読み込みのタスク全体が完了しています。ルートコンポーネントのデータ要件を満たすためにAPIルートは必要ありません。ルート自体が既に独自のAPIとなっています。

ナビゲーション以外でのローダーの呼び出し

しかし、ユーザーがそのルートを訪れているわけではなく、現在のページが何らかの理由でそのルートのデータを必要としているため、ローダーからデータを取得したい場合があります。明確な例として、データベースからレコードを検索してユーザーに推薦する<Combobox>コンポーネントがあります。

このような場合には、useFetcherを使用できます。ブラウザ上のRemixはサーバー上のRemixと通信する方法を知っているため、データを取得するために多くの作業は必要ありません。Remixのエラー処理が機能し、競合状態、中断、フェッチのキャンセルも自動的に処理されます。

例えば、検索を処理するルートを以下のように作成できます:

app/routes/city-search.tsx
export async function loader({
request,
}: LoaderFunctionArgs) {
const url = new URL(request.url);
return json(
await searchCities(url.searchParams.get("q"))
);
}

そして、Reach UIのコンボボックス入力とuseFetcherを組み合わせて使用します:

function CitySearchCombobox() {
const cities = useFetcher();
return (
<cities.Form method="get" action="/city-search">
<Combobox aria-label="Cities">
<div>
<ComboboxInput
name="q"
onChange={(event) =>
cities.submit(event.target.form)
}
/>
{cities.state === "submitting" ? (
<Spinner />
) : null}
</div>
{cities.data ? (
<ComboboxPopover className="shadow-popup">
{cities.data.error ? (
<p>都市の読み込みに失敗しました :(</p>
) : cities.data.length ? (
<ComboboxList>
{cities.data.map((city) => (
<ComboboxOption
key={city.id}
value={city.name}
/>
))}
</ComboboxList>
) : (
<span>結果が見つかりません</span>
)}
</ComboboxPopover>
) : null}
</Combobox>
</cities.Form>
);
}

リソースルート

他の場合には、アプリケーションの一部ではあるものの、アプリケーションのUIの一部ではないルートが必要になることがあります。例えば、レポートをPDFとしてレンダリングするローダーが必要かもしれません:

export async function loader({
params,
}: LoaderFunctionArgs) {
const report = await getReport(params.id);
const pdf = await generateReportPDF(report);
return new Response(pdf, {
status: 200,
headers: {
"Content-Type": "application/pdf",
},
});
}

ルートがRemix UI(<Link>useFetcherなど)によって呼び出されず、デフォルトのコンポーネントをエクスポートしていない場合、それは汎用のリソースルートとなります。GETで呼び出された場合はローダーのレスポンスを返し、POSTPUTPATCHDELETEで呼び出された場合はアクションのレスポンスを返します。

以下に、考えられるユースケースをいくつか示します:

  • モバイルアプリケーション用のJSON API(サーバーサイドコードをRemix UIで再利用)
  • 動的なPDFの生成
  • ブログ記事やその他のページ用のソーシャル画像の動的生成
  • 他のサービス用のWebhook

詳細はリソースルートのドキュメントをご覧ください。