
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 ã€ã®å¢çïŒ

| é
ç® | å¢çã®æ±ºãæ¹ |
| :----------- | :------------------------------------------------- |
| ããªããŒã·ã§ã³ | 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 ã«ããå°ããªå¢çãäžã€ã決ããŠã¿ãŠã»ããã§ãã
