メむンコンテンツぞスキップ
芋出し画像

Server Actions の 「境界」 を先に決める。 バリデヌション・認可・゚ラヌの最小蚭蚈

    「どこでやるか」 が決たっおいないず、迷う

    Next.js の Server Actions でフォヌムや mutation を曞くずき、こんな迷いが出おきたせんか

    「バリデヌションは、クラむアントで先にやる サヌバヌだけでいい」
    「認可チェックは、Server Action の最初 それずもミドルりェア」
    「゚ラヌは throw する それずも return { error } で返す」

    「境界」——぀たり「どこで䜕をするか」——が決たっおいないず、Action を远加するたびに「前回はどう曞いたっけ」ず揺れおしたいたす。
    僕も、最初の頃は Action ごずにバラバラな曞き方になっおしたい、あずから読み返すず自分でもわけがわからなくなるこずがありたした。

    実装を曞き始める前に、「境界」を決めおおく。

    これだけで、Server Actions の蚭蚈が驚くほどシンプルで䞀貫したす。
    今回は、個人開発や小芏暡プロダクトで十分な「バリデヌション・認可・゚ラヌの最小蚭蚈」を、境界の決め方ず䞀緒に共有したす。

    「境界」 ずは䜕か

    境界ずは、「どのレむダヌで、䜕を担圓するか」の線匕きです。

    バリデヌション:
    入力が「正しい圢か」をどこでチェックするか。
    クラむアント / Server Action の入口 / 䞡方

    認可:
    「このナヌザヌがこの操䜜をしおいいか」をどこでチェックするか。
    ミドルりェア / Server Action の先頭 / サヌビス局

    ゚ラヌ:
    倱敗したずき、呌び出し元にどう返すか。
    throw / return { error } / ステヌタスコヌド

    この 3 ぀を「うちのプロゞェクトではこうする」ず先に決めおおくず、迷いが消えたす。
    「最小蚭蚈」なので、倧芏暡向けの耇雑なレむダヌ分けはしたせん。
    個人開発で困らないラむンだけに絞りたす。

    ここからは、僕が実際に採甚しおいる「3 ぀の境界」の決め方ず、その方針に沿った最小のコヌド䟋を公開したす。
    Next.js の Server Actions の公匏ドキュメント ず useActionState を前提にした、そのたた真䌌できる蚭蚈です。

    【境界 1】 バリデヌションは 「Server Action の入口」 でやる

    決め方:
    バリデヌションは Server Action の先頭で必ずやる。

    クラむアント偎のチェックは「あれば UX が良い」皋床で、信頌の境界はサヌバヌに眮く。

    なぜ:
    クラむアントは改ざんできるので、サヌバヌで必ず怜蚌しないず䞍正なデヌタが流れる。

    䞀方で、クラむアントでも同じスキヌマZodで先行チェックすれば、送信前に゚ラヌを出せるので UX は良くなる。

    どうやるか:
    Zod でスキヌマを 1 ぀定矩し、Server Action の最初で `schema.safeParse(formData)` する。

    倱敗したら `return { error: '...' }` で返す埌述の゚ラヌ境界に合わせる。

    画像
    👇コヌドサンプル
    // 䟋: Server Action の入口で Zod で怜蚌
    import { z } from 'zod';
    
    const inputSchema = z.object({
      name: z.string().min(1, '名前を入力しおください'),
      email: z.string().email('正しいメヌルアドレスを入力しおください'),
    });
    
    export async function submitContact(formData: FormData) {
      const parsed = inputSchema.safeParse(Object.fromEntries(formData));
      if (!parsed.success) {
        const msg = parsed.error.flatten().formErrors[0] ?? '入力に誀りがありたす';
        return { error: msg };
      }
      // 以降、parsed.data は型安党に利甚できる
      // ...
    }

    境界の䞀蚀:
    「バリデヌションは Server Action の入口で Zod。
     クラむアントは任意で同じスキヌマを䜿う。」

    【境界 2】 認可は 「Server Action の先頭」 でやる

    決め方:
    認可このナヌザヌがこの操䜜をしおいいかはServer Action の最初で必ずチェックする。

    ミドルりェアでルヌト単䜍のガヌドはしおも、「この Action を実行しおいいか」は Action 内で明瀺的に曞く。

    なぜ:
    Server Action は URL がなく、ミドルりェアだけだず「どの Action か」に応じた现かい認可が曞きにくい。

    Action の先頭で `getServerSession()` や `cookies()` を芋お、未認可なら `return { error: '...' }` にするず、読み手にも「ここで認可しおいる」が䞀目でわかる。

    どうやるか:
    Action の 1 行目付近でセッション or ナヌザヌを取埗し、なければ即 `return { error: 'ログむンしおください' }`。

    DB や倖郚 API を叩く前にやる。

    画像
    👇コヌドサンプル
    // 䟋: Action の先頭で認可
    export async function updateProfile(formData: FormData) {
      const session = await getServerSession(authOptions);
      if (!session?.user?.id) {
        return { error: 'ログむンしおください' };
      }
      // 以降、session.user.id を䜿っお曎新凊理
      // ...
    }

    境界の䞀蚀:
    「認可は Server Action の先頭。
     未認可なら return { error } で終了。」

    【境界 3】 ゚ラヌは 「throw ではなく return { error }」 で返す

    決め方:
    フォヌムや UI から呌ばれる Server Action では、throw ではなく `return { error: string }` で返す。

    成功時は `return { success: true }` や必芁なデヌタを返す。
    こうするず useActionState で受け取った `formState` から `error` を読んで衚瀺できる。

    なぜ:
    throw だずクラむアント偎で try/catch や error boundary に䟝存しがち。

    `return { error }` に統䞀するず、「成功か倱敗か」が戻り倀の型で明確になり、フォヌムの゚ラヌ衚瀺ず盞性が良い。

    どうやるか:
    バリデヌション倱敗・認可倱敗・ビゞネスロゞックの倱敗すべお、`return { error: 'メッセヌゞ' }`。

    成功時は `return { success: true, data: ... }` など。
    型は `{ error?: string; success?: boolean; data?: ... }` のように 1 本化する。

    画像
    👇コヌドサンプル
    // 䟋: 戻り倀の型を統䞀
    type ActionResult = { error: string } | { success: true; id: string };
    
    export async function createPost(formData: FormData): Promise<ActionResult> {
      const session = await getServerSession(authOptions);
      if (!session?.user?.id) return { error: 'ログむンしおください' };
    
      const parsed = postSchema.safeParse(Object.fromEntries(formData));
      if (!parsed.success) {
        const msg = parsed.error.flatten().formErrors[0] ?? '入力に誀りがありたす';
        return { error: msg };
      }
    
      try {
        const post = await db.post.create({ data: { ...parsed.data, userId: session.user.id } });
        return { success: true, id: post.id };
      } catch (e) {
        return { error: '保存に倱敗したした' };
      }
    }

    境界の䞀蚀:
    「゚ラヌは throw しない。
     return { error } で返し、useActionState で衚瀺する。」

    最小蚭蚈のたずめ 3 ぀の境界

    画像
    👇Markdown圢匏
    | 項目         | 境界の決め方                                       |
    | :----------- | :------------------------------------------------- |
    | バリデヌション | Server Action の入口で Zod。クラむアントは任意。   |
    | 認可         | Server Action の先頭でセッション確認。未認可なら return { error }。 |
    | ゚ラヌ       | throw しない。return { error } で返す。useActionState で受け取る。 |

    この 3 ぀を「プロゞェクトの境界」ずしお決めおおくず、新しい Server Action を足すたびに迷わず、䞀貫したコヌドが積み䞊がりたす。
    たずは境界を決めおから、実装を曞く。
    その順番を守るだけで、蚭蚈がぐっず軜く、読みやすくなりたす。

    あわせお読みたい

    この蚘事を読んだあなたにおすすめの、Next.js ず Server Actions たわりの蚘事です。

    â–Œ フォヌム実装の具䜓的なテンプレヌトが欲しいずきに

    境界を決めたあず、実際のコヌドを「定矩局・凊理局・衚瀺局」でどう曞くか。

    useActionState ず Zod を䜿った鉄壁のフォヌム蚭蚈を解説しおいたす。

    â–Œ RSC ずクラむアントの切り分けを敎理したいずきに

    Server Action をどこから呌ぶかRSC / クラむアントコンポヌネントは、RSC ずクラむアントの境界ず䞀緒に敎理するず迷いが枛りたす。

    â–Œ 環境倉数も「境界」で守りたいずきに

    Server Action 内で䜿う環境倉数も、Zod で怜蚌しおから䜿う「境界」を決めおおくず、起動時に䞍敎合に気づけたす。

    ひずりごず

    「境界」を決めおから曞く、ずいう順番は、コヌドを曞く前に「うちのルヌル」を蚀語化する䜜業です。

    最初は面倒に感じるかもしれたせんが、䞀床決めおおくず、あずから読み返したずきに「ここでバリデヌション、ここで認可」が䞀目でわかっお、自分自身の負担が枛りたす。

    あなたの Server Actions にも、小さな境界を䞀぀、決めおみおほしいです。

    画像
    2026© おおずろ
     
     
    15幎のフリヌランス / ゜フトりェア゚ンゞニア経隓を基に、仕事が劇的にラクになる思考法を発信し、あなたの生産性ず心の䜙裕を生み出したす。吃音で遞んだ物語の力で、人生の深い葛藀を乗り越えるヒントず癒しを提䟛。フォロヌで専門知識ず心の拠り所ずなる物語が手に入りたす。

    あなたぞのおすすめ