「フォーム送信をサーバー側で処理する Server Action を書いて」と Antigravity に頼んで、出てきたコードをそのまま貼り付けたら、Zod バリデーションも revalidatePath も認可チェックも一切入っていなかった——同じ経験をした方は、たぶん私だけではないはずです。Server Actions は書くのが楽な分、AI に頼むときの曖昧さが、そのままプロダクションのセキュリティホールに直結しやすい領域です。
ここではAntigravity の AI エージェントに Server Actions を書かせる際に最低限「外してはいけない」ガードを、依頼の仕方と確認のチェックリストとしてまとめました。Next.js 15 App Router を前提にしますが、同様の問題は Remix や SvelteKit のフォームアクションでも起こります。
なぜ AI 生成の Server Actions は危険になりがちなのか
Server Actions は "use server" 一行でブラウザから直接呼び出せる関数になります。これはとても便利な反面、ブラウザから渡ってきた値を完全に信用してはいけない関数でもあります。AI に「フォーム値を受け取って DB に保存する Server Action」と頼むと、最短経路で動くコードを返してくれますが、その「最短経路」には以下が含まれていないことが多いのです。
- フォーム値の型・必須チェック(Zod など)
- ログイン中ユーザーの認証チェック
- そのリソースを編集する権限を持っているかの認可チェック
- 完了後の
revalidatePath/revalidateTag呼び出し - エラーハンドリング(DB 例外をクライアントに漏らさない)
私の経験上、Antigravity に何も指定せずに Server Action を頼むと、5 つのうち 1〜2 つしか入りません。これを毎回手動で指摘していると、AI に頼んでいる時間より「監査して書き足す時間」のほうが長くなり、結局自分で書いた方が早いという状況になります。
依頼テンプレートを 1 個用意するだけで防げる
私は Antigravity の .rules または AGENTS.md に、Server Action を書かせるとき専用の短いテンプレートを置いています。これだけで漏れがほぼゼロになりました。
# Server Action 依頼テンプレート
新しい Server Action を作るとき、以下を必ず含めてください:
1. ファイル冒頭に "use server" を記述
2. 入力は Zod スキーマで定義し、parse() で検証する
3. auth() で現在のユーザーを取得し、未認証なら early return
4. リソース編集系は、対象オブジェクトの所有者と userId が一致するか必ず確認する
5. DB 操作後は revalidatePath() または revalidateTag() を呼ぶ
6. try/catch で DB 例外を握り、{ ok: false, message: "..." } を返す
7. クライアントに DB エラーの生メッセージを返さないこれを AGENTS.md に書いておくと、Antigravity のエージェントは新しい Server Action を作るたびに自動でこのチェックリストを参照します。「フォームから記事を投稿する Server Action を作って」と頼むだけで、一発でほぼ正しい形が出てくるようになります。
実際の Server Action の最小安全形
テンプレートに従って Antigravity に書かせると、たとえば「記事を投稿する」アクションは次のような形に着地します。これは私が実際にプロダクションで使っている雛形をシンプルにしたものです。
// app/actions/posts.ts
"use server";
import { z } from "zod";
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
const PostInput = z.object({
title: z.string().min(1).max(120),
body: z.string().min(1).max(20_000),
});
export async function createPost(_prev: unknown, formData: FormData) {
// 1. 認証チェック
const session = await auth();
if (!session?.user) {
return { ok: false, message: "ログインが必要です" } as const;
}
// 2. 入力バリデーション
const parsed = PostInput.safeParse({
title: formData.get("title"),
body: formData.get("body"),
});
if (!parsed.success) {
return {
ok: false,
message: "入力に問題があります",
issues: parsed.error.flatten().fieldErrors,
} as const;
}
// 3. DB 操作(例外は握る)
try {
await db.post.create({
data: { ...parsed.data, authorId: session.user.id },
});
} catch (e) {
console.error("createPost failed", e);
return { ok: false, message: "保存に失敗しました" } as const;
}
// 4. キャッシュ無効化
revalidatePath("/posts");
return { ok: true } as const;
}ポイントは「認証 → 入力検証 → DB 操作 → revalidate」という 4 段階の順番が常に同じになることです。AI が書く Server Action のレビューをするときは、この 4 段階に欠けがないかだけを見ればよくなり、認知負荷が一気に下がります。
クライアント側は useActionState で受ける
返り値の型が { ok: boolean } で揃っていると、useActionState でのハンドリングも揃って書けます。フォームのエラー表示と成功時の挙動を、毎回似たようなコードでつなげるだけで済みます。
"use client";
import { useActionState } from "react";
import { createPost } from "@/app/actions/posts";
export function PostForm() {
const [state, formAction, pending] = useActionState(createPost, null);
return (
<form action={formAction}>
<input name="title" required />
<textarea name="body" required />
<button disabled={pending}>{pending ? "送信中..." : "投稿する"}</button>
{state && !state.ok && <p role="alert">{state.message}</p>}
{state?.ok && <p>投稿しました</p>}
</form>
);
}ここで重要なのは、Server Action 側が issues フィールドにフィールドごとのエラーを返してくれていることです。クライアントで state?.issues?.title?.[0] のようにアクセスして、各入力欄の下に個別エラーメッセージを出せます。Antigravity に「フィールドごとのエラー表示も追加して」と一行頼むだけで、ここまで広げてくれます。
Antigravity でレビューを定型化する
Server Action を書いた後の確認も、AI に任せた方が速いです。私は新しいアクションを書いたらインラインチャット(Cmd+K)で次のように頼んでいます。
このファイルの Server Action について、以下の観点でレビューして:
- "use server" が冒頭にあるか
- Zod でバリデーションしているか
- 認証チェックがあるか
- 認可チェックがあるか(リソースの所有者一致確認)
- revalidatePath / revalidateTag があるか
- DB 例外を握っているか
- エラーメッセージにスタックトレースや SQL が漏れていないか
問題があれば具体的に指摘し、欠けがあれば修正パッチを diff で出して。
このプロンプトをスニペット化しておくと、Server Action を新規追加したコミットごとに 30 秒で監査が走ります。漏れが見つかったらそのまま Apply するだけです。Server Action は短いから AI でも全体を見渡せるという特性が、ここでうまく効いてくれます。
実装の方針をさらに体系的に深めたい方は、関連する記事として Antigravity × Zod でスキーマ駆動開発を加速する や、Webhook 受信エンドポイントの安全な作り方をまとめた AntigravityでWebhook受信エンドポイントを取りこぼしなく作る実践パターン を合わせて読むと、サーバー入力の信頼境界を扱う考え方が一気にそろいます。
書籍で体系的に
次の一歩
新しく書く Server Action を 1 つ選び、上のテンプレートを AGENTS.md に貼り付けたうえで Antigravity に書き直してもらってください。出てきたコードを上のレビュープロンプトで監査するところまでを 1 セットにすると、それ以降「書く時に毎回考える項目」が 7 つから 0 つに減ります。Server Action は気軽に増えるからこそ、最初の 1 つで型を作っておくと、その後のすべてのアクションがその恩恵を受けます。