ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-05-02中級

Antigravity に Server Actions を書かせるときの落とし穴 — Zod・revalidate・セキュリティを後付けしない実装フロー

Antigravity の AI に Next.js の Server Actions を書かせると、バリデーション・revalidate・認可チェックがごっそり抜け落ちることがあります。それを後付けにせず、最初から安全な実装に着地させるための実践フローをまとめました。

antigravity435nextjs4server-actionszod2security13app-dev47

「フォーム送信をサーバー側で処理する 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 つで型を作っておくと、その後のすべてのアクションがその恩恵を受けます。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

アプリ開発2026-06-25
「広告を見たら壁紙を解放」をエージェントがクライアント側だけで付与していた — リワード報酬をSSVで検証し直した記録
リワード広告を見たら壁紙を解放する機能を Antigravity のエージェントに頼んだら、解放フラグをクライアント側だけで書き込む実装が返ってきました。なぜそれでは足りないのか、AdMob のサーバーサイド検証(SSV)で報酬付与を検証し直し、二重付与まで止めた設計を記録します。
アプリ開発2026-07-13
画像の一括最適化をエージェントに任せる前に置く、見た目の劣化を止めるゲート
壁紙アプリの数百枚のアセットをエージェントに一括再エンコードさせたら、数枚だけ色がくすんでいました。サイズ削減率だけでは劣化を見抜けません。SSIM・ΔE・ファイルサイズの3軸でマージ前に不良変換を弾くゲートの設計と、Pillow と scikit-image で動く判定スクリプトをまとめます。
アプリ開発2026-07-09
エージェントが直した .proto がバイナリ互換を壊す前に — buf breaking で wire 互換を門番する
エージェントに .proto を編集させると、テキストとしては正しくても wire 互換を静かに壊すことがあります。フィールド番号の再利用と型変更をどう機械で止めるか、buf breaking と reserved を組み合わせたゲートを動くコードで示します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →