Next.js Route Handlersで303 See Otherのリダイレクトを実装してみる
この記事は、Money Forward Engineering Advent Calendar 2023 14日目の投稿です。
前日は @tosite(てっしーさん)の必見!?JaSST'23 Kyushuの舞台裏全部お見せしちゃいますでした。
こんにちは!マネーフォワードPay事業本部 プロダクト開発部 クライアントグループ フロントエンドチーム のひめのです。
今回は、Next.jsのRoute Handlersでの303 See otherでのリダイレクトを実装してみることで、App RouterとRoute Handlersについての理解を深めていこうと思います。
303 See Otherとは
303 See Otherは、サーバーがクライアントに対して、他のページへリダイレクトすることを示すレスポンスコードです。
このコードは主にフォーム送信で使用され、POSTリクエストを実行した後に、クライアントをGETリクエストによって別のページにリダイレクトさせるために使用されることがあります。
詳しくは、MDNやRFC7231をご参照ください。
リダイレクトの種類
ステータスコードごとのリダイレクトの取り扱いは、仕様としてRFCに定義されています。
しかし、実際のブラウザの挙動には違うものが存在します。(301/302)
また、リダイレクトのおおまかな種類として恒久的(301/308)か一時的(302/303/307)かという違いもあります。
詳細についてはMDNがとてもわかりやすいので、リダイレクトのステータスコードに困った場合は参考にすると便利です。
Route Handlersとは
次に、Next.js App Routerから導入されたRoute Handlersについてです。
Next.jsをよく利用しているユーザー向けに簡単に説明すると、Pages Routerで存在していたAPI Routesにあたるものです。
API Routesとの違いについて、簡単に表にまとめました。
特徴 | API Routes (Pages Router) | Route Handlers (App Router) |
---|---|---|
ディレクトリ | /pages/api |
/app |
ファイル名 | 任意 |
route.ts / route.js
|
HTTPメソッドの扱い | ひとつのハンドラー関数でメソッドごとにケース分け | HTTPメソッドごとに関数がわかれる (GET , POST , DELETE など) |
利用可能なAPI |
NextApiRequest NextApiResponse
|
NextApiRequest NextApiResponse Fetch APIの Request Response
|
ためしてみる
ある程度前提となる知識を確認したところで、早速簡単なフォームをつくっていきたいと思います。
実行環境
Node.jsは v20.9.0
、Next.jsは v14.0.4
を利用して進めています。
また、ブラウザはGoogle Chrome 119で確認しています。
準備
まずは、Next.jsを導入しましょう。
$ npx create-next-app --typescript .
ディレクトリ名などは適宜変更してください。今回は以下のように配置しました。
✔ What is your project named? … next-see-other
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
後々スタイルが見づらくなるため、 global.css
の色味設定を消しておきます。
@@ -1,27 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
-
-:root {
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- }
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
-}
この状態で、ローカルサーバーが立ち上がるのを確認しましょう。
$ npm run dev
フォームをつくる
/form-action/submit
というエンドポイントにPOSTリクエストを送るという簡単なフォームを /src/app/form-action/page.tsx
につくります。
const FormActionPage = () => {
return (
<main>
<form
action={"/form-action/submit"}
method="POST"
className="grid gap-4 border border-slate-700 rounded m-4 p-4"
>
<button
className="border border-slate-700 rounded-sm text-slate-700 p-2"
type="submit"
>
Post Text
</button>
</form>
</main>
);
};
export default FormActionPage;
このようなとても簡素なページができます。
この状態では、「Post Text」をクリックしてもページが存在しないため、Next.jsが用意している404ページが表示されます。
POST /form-action/submit
を実装する
つぎに、Route Handlersを用いて303リダイレクトを返す関数をつくります。
import { NextApiRequest, NextApiResponse } from "next";
import { NextResponse } from "next/server";
export const POST = async (_req: NextApiRequest, _res: NextApiResponse) => {
// 必要があれば、submit時のFormDataを取得してDBに保存する処理などを追加
return NextResponse.redirect(
new URL("http://localhost:3000/form-action/result"),
{
status: 303,
}
);
};
NextResponse#redirect
は、2つ目の引数に ResponseInit
を渡すことができるため、ステータスコードを変更することが可能です。
型は以下になります。
static redirect(url: string | NextURL | URL, init?: number | ResponseInit): NextResponse<unknown>;
結果に表示するページを用意する
ここまででフォームのsubmitは送信されますが、リダイレクト先のページを簡単につくります。
const ResultPage = () => {
return (
<div className="p-4">
<h1>Result Page</h1>
</div>
);
};
export default ResultPage;
動作を確認する
実際に、フォームの動作を確認してみましょう。
「Post Text」をクリックすると、 /form-action/result
へ遷移します。
ChromeのDevToolsで「Network」タブを開き、submitの列を確認すると、以下が確認できます。
- ステータスコードが303 See otherになっている
- Locationに
http://localhost:3000/form-action/result
が設定されている
ここまでで、Route Handlersを用いたリダイレクトの実装を一通り実装できました。
おつかれさまでした!
さいごに
Server Actionsもそうですが、Route Handlersによっても、Next.jsで表現できる幅が広がったように感じます。
キャッシュの理解やServer Componentの役割など、まだまだ学ぶことが多くて楽しみです。
最後までお読みいただきありがとうございました。
明日はmasami moritaさんの記事になります!
Discussion