はじめに
Next.jsのApp Routerで、フォームから送信したデータをServer Action経由で保存する処理を実装したのに、保存自体は成功しているのに一覧画面に反映されない、というつまずきに出会ったことはないでしょうか。
Claude CodeにCRUD機能を実装してもらうと、Server Actionでデータベースへの保存処理は書かれているのに、保存後にキャッシュを更新する処理が抜けていることがあります。本記事では、この症状が起きる理由と直し方を紹介します。
症状
以下のようなコードで、一覧ページと投稿フォームを作ったとします。
// app/posts/page.tsx(一覧ページ、Server Component)
import { getPosts } from "@/lib/posts";
export default async function PostsPage() {
const posts = await getPosts();
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
// app/posts/actions.ts(Server Action)
"use server";
import { supabase } from "@/lib/supabase";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
await supabase.from("posts").insert({ title });
}
このコードで投稿を送信すると、Supabase側にはきちんとデータが保存されます。しかし、一覧ページに戻っても、新しい投稿が表示されないことがあります。もう一度ブラウザを再読み込みすると、今度は表示される、という挙動になることもあります。
原因
Next.jsのApp Routerは、パフォーマンスのためにページの内容をキャッシュする仕組みを何層か持っています。ブラウザ側で直前に表示したページの内容を一時的に保持しておく仕組み(Router Cache)もその一つです。「保存はできているのに、戻ってきた一覧だけ古いまま。再読み込みすると直る」という挙動は、この手元側のキャッシュが更新されずに残っているケースでよく見られます(キャッシュの挙動はNext.jsのバージョンや取得方法によって変わる部分もあるため、手元の環境で実際にどの層が古い内容を返しているかは、確認しながら切り分けるのが確実です)。
いずれにせよ、Server Actionでデータベースの中身を変更しても、Next.jsはそれだけでは「このページのキャッシュは古くなった」と自動では判断してくれません。データベース側の変更と、Next.js側のキャッシュは、別々に管理されているためです。そのため、保存処理自体は成功していても、一覧ページはキャッシュされた古い内容のまま表示され続けることがあります。
直し方
Server Actionの中で、更新対象のパスに対してrevalidatePathを呼び出します。
// app/posts/actions.ts(修正後)
"use server";
import { supabase } from "@/lib/supabase";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
await supabase.from("posts").insert({ title });
revalidatePath("/posts");
}
revalidatePath("/posts")を呼ぶことで、「/postsのキャッシュは古くなったので、次に表示するときは取得し直してください」とNext.jsに伝えられます。これにより、投稿を送信した直後に一覧へ戻っても、最新のデータが表示されるようになります。
複数のページで同じデータを表示している場合(一覧ページと詳細ページなど)は、それぞれのパスに対してrevalidatePathを呼ぶ必要があります。fetch()で直接データ取得している場合は、取得時にtagsを設定してrevalidateTagでまとめて無効化する方法もありますが、Supabaseクライアントを使っている今回の構成では対象外です。
AIに実装を確認してもらうときの聞き方
Server ActionでDBを更新するコードを書いてもらったら、「このServer Actionの中で、更新後にrevalidatePathかrevalidateTagは呼ばれていますか?」と一言確認するだけで、この種の見落としはその場で気づけます。
まとめ
- Server Actionでデータベースを更新しても、Next.jsのキャッシュは自動では更新されない
-
revalidatePath(またはrevalidateTag)を呼ぶことで、該当ページのキャッシュを次回アクセス時に取得し直させることができる - 保存処理自体は成功して見えるため、動作確認で気づきにくいポイント
未経験者向けの講座を運営しています
未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を、全20セッションで体系化した教材です。Claude Code を学習パートナーにする CLAUDE.md と学習モード(learner / developer)の設計までセットで含みます。
- ▶ ライブデモ(インストール不要・無料で今すぐ見られます)→ https://trial-web-ayies128s-projects.vercel.app/?ref=qiita
- 教材完全版+月5,500円のメンタリング(全20セッション+チャット質問し放題)→ https://menta.work/plan/20251?ref=qiita
- 無料体験版(git clone して自分の手元で動かす・最初の数セッション分・⭐ Star もよろしくお願いします)→ https://github.com/ayies128/next-ai-camp-trial
- YouTube『AIエンジニア情報局』(AI×開発ニュースを1本5分でキャッチアップできる別運営チャンネル・無料)→ https://www.youtube.com/channel/UC1rXVD9WYsQPQEWZyd-A1KA/?ref=qiita
※ Qiita読者の方には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。