- Server Actions הן פונקציות שרץ בצד השרת שאפשר לקרוא להן ישירות מקומפוננטות React (גם Client Components), בלי צורך להגדיר API route או route handler נפרד.
- השימוש המעשי מסומנים בהוראת 'use server' בראש הפונקציה או הקובץ, ואפשר להפעיל אותם מ-event handlers (onSubmit, onClick) או מטופס דרך action prop.
- היתרונות פחות JavaScript בלקוח, אפשרות לעבוד גם כש-JS כבוי בדפדפן (progressive enhancement), ופישוט תהליך הפיתוח של mutations במסד הנתונים.
- מתי להשתמש מעולים ל-mutations פשוטים (יצירה, מחיקה, עדכון); ל-public APIs או לוגיקה מורכבת בצד-לקוח, route handlers (api/) עדיין המתאים יותר.
Server Actions במבט כללי
Server Actions הם פיצ'ר של React ו-Next.js שמאפשר להגדיר פונקציות שרץ בצד השרת ולקרוא להן ישירות מקומפוננטות React - גם מקומפוננטות לקוח (Client Components) - בלי להגדיר API route או route handler נפרד. הן יציבות מ-Next.js 14 (אוקטובר 2023), אחרי שלב RC ב-Next.js 13.4 (מאי 2023).
הסימון מתבצע באמצעות הוראת 'use server' בראש הפונקציה או הקובץ, ו-Next.js דואג להעביר את הקריאה לשרת אוטומטית - בלי שתצטרכו לכתוב fetch ידנית. השימוש הטיפוסי הוא ב-mutations: יצירה, עדכון או מחיקה של נתונים. היתרון המרכזי: פחות JavaScript בלקוח, פישוט הקוד, ותמיכה ב-progressive enhancement (הטופס עובד גם כש-JavaScript בדפדפן כבוי).
דוגמה מהחיים האמתיים
תארו לעצמכם את התרחיש הבא: משתמשי האפליקציה שלכם רוצים ליצור פוסט חדש (ממש כמו בפייסבוק), וברגע שהם לוחצים על "פרסם", אתם רוצים לשלוח את הנתונים האלו מן הסתם לשרת וכמובן לשמור את הנתונים האלה במסד הנתונים. בדרך המסורתית הייתם צריכים לבצע כמה שלבים:
ליצור API route: בתהליך המסורתי, הייתם מגדירים endpoint בשרת (כמו /api/create-post) שיקבל את הנתונים מהטופס, ויטפל בלוגיקה של שמירת הנתונים במסד הנתונים. זה דורש הגדרת קובץ חדש, כתיבת לוגיקה לקבלת הבקשה, ולבסוף שמירת הנתונים (ואולי עוד כמה דברים בדרך).
לכתוב בקשת fetch: בתוך הקומפוננטה שלכם בצד הלקוח, הייתם צריכים לכתוב בקשת fetch או להשתמש ב-Axios (או כל כלי אחר) כדי לשלוח את הנתונים ל-endpoint שיצרתם.
עם Server Actions, אתם יכולים לדלג על הצורך בהגדרת API route ובשליחת בקשות fetch. במקום זאת, אתם מגדירים פונקציה בצד השרת (Server Action) שתטפל בשמירת הנתונים. את הפונקציה הזו תוכלו להגדיר בקובץ נפרד ולהשתמש בה בתוך קומפוננטות צד לקוח או צד שרת. הנה דוגמה:
// src/serverActions/post/createPost.ts
'use server'
export async function createPost(postData) {
// Simulate a server-side database interaction for post creation
await collection.insertOne(postData);
return { message: 'Post created' };
}
// src/app/SomePage.tsx
'use client'
import { useState } from 'react';
export default function MyComponent() {
const [message, setMessage] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
const { title, content } = event.target;
const result = await createPost({
title: title.value,
content: content.value
});
setMessage(result.message);
};
return (
<form onSubmit={handleSubmit}>
<input name="title" placeholder="Title" />
<textarea name="content" placeholder="Content" />
<button type="submit">Submit</button>
{message && <p>{message}</p>}
</form>
);
}
אנו מגדירים Server Actions באמצעות ה-Directive 'use server': זה מסמן ל-Next.js שהפונקציה createPost צריכה לרוץ על השרת ולא בדפדפן.
הדרכים הנכונות להשתמש ב-Server Actions
כשמדובר ב-Server Actions, חשוב לזכור שהקוד רץ בצד השרת, ולכן יש כמה עקרונות שחשוב לקחת בחשבון. ראשית, שליפת נתונים ברינדור ראשוני: אם הקומפוננטה דורשת נתונים עם טעינתה הראשונה, כדאי לבצע את השליפה הזו בצד השרת, לפני הרינדור, ולהעביר את הנתונים כ-props ל-client component.
export default async function PostManagement() {
const response = await fetch('http://localhost:3001/posts');
const data = await response.json() as IPost[];
return (
<div>
<ClientComponent data={data} />
</div>
);
}שנית, אפשר לשלב Server Actions עם events כמו onSubmit או onClick. כך הקומפוננטה יכולה להפעיל את ה-Server Action בהתאם לפעולות המשתמש, ולהגיב לאינטראקציות שונות.
יתרונות של Server Actions
פשטות בתהליך העבודה
Server Actions מאפשרים לכם לכתוב קוד שרץ על השרת ישירות מתוך הקומפוננטות שלכם. לדוגמה, אם אתם רוצים לשמור נתונים של פוסט חדש במסד הנתונים לאחר שמשתמש לוחץ על כפתור "פרסם", אתם יכולים לכתוב את כל הלוגיקה הזו בתוך הקומפוננטה עצמה, בלי צורך ליצור API route נפרד. זה הופך את העבודה למהירה ופשוטה יותר.
פחות JavaScript בצד הלקוח
כשמשתמשים ב-Server Actions, אתם מפחיתים את הצורך להריץ קוד JavaScript בצד הלקוח (כלומר בדפדפן). בדרך כלל, כשאתם רוצים לשלוח נתונים מהלקוח לשרת, כמו במקרה של טופס שבו משתמש שולח פוסט חדש, אתם משתמשים בבקשת fetch שנעשית דרך JavaScript.
השימוש ב-Server Actions מבטל את הצורך הזה, כי הקוד שרץ על השרת מתבצע ישירות מתוך הקומפוננטה ב-React. כתוצאה מכך, הדפדפן צריך להריץ פחות קוד, וזה יכול להוביל לטעינה מהירה יותר של הדפים ולשיפור בביצועים של האתר.
עבודה חלקה גם בלי JavaScript
במצבים שבהם JavaScript לא נטען במלואו או כבוי בצד הלקוח, אפליקציות שמשתמשות ב-Server Actions יכולות להמשיך לתפקד בצורה תקינה. למשל, אם משתמש מנסה לשלוח טופס וה-JavaScript בדפדפן שלו לא פועל, השרת עדיין יכול לטפל בפעולה הזו דרך ה-Server Actions, כי היא מתבצעת על השרת ולא בדפדפן. זה הופך את האפליקציה לעמידה יותר ומבטיח שהיא תפעל בצורה טובה גם בתנאים פחות אופטימליים.
חסרונות של Server Actions
לא מתאים לכל סוגי הפרויקטים
Server Actions מתאימים יותר לפעולות פשוטות כמו שמירת נתונים או שליחה לשרת. אם אתם צריכים לנהל הרבה לוגיקה בצד הלקוח, ייתכן שהשיטה המסורתית של API routes תהיה מתאימה יותר.
קושי בניהול קוד בפרויקטים גדולים
בפרויקטים גדולים, השימוש ב-Server Actions יכול לטשטש את הגבולות בין הקוד שרץ על השרת לקוד שרץ על הלקוח, מה שעלול להקשות על ניהול הקוד בצורה ברורה ומסודרת.
דעתי האישית
אני אשתמש ב-Server Actions בעיקר למשימות פשוטות עם מינימום לוגיקה, כמו הוספת To-Do פשוט לרשימה. עם זאת, אפילו במקרים כאלה אני אעדיף להשתמש בשיטה המסורתית ולהפריד בין השרת ללקוח וליצור endpoint ייעודי משלו. הגישה הזו מאפשרת לי לשמור על מבנה קוד ברור ומאורגן וכמובן על הפרדה מוחלטת בין הלוגיקה בצד השרת לצדו של הלקוח.
שאלות נפוצות
מה ההבדל בין Server Actions ל-API Routes
API Routes (או Route Handlers ב-App Router) הם endpoints מפורשים שמוגדרים בתיקיית /api/, מקבלים HTTP request ומחזירים response, וניתנים לקריאה מכל מקום (גם מאפליקציות אחרות). Server Actions הם פונקציות JavaScript רגילות עם 'use server', שאפשר לקרוא להן ישירות מקומפוננטה כאילו הן פונקציה רגילה - Next.js דואג להעביר את הקריאה לשרת אוטומטית. Server Actions פשוטים יותר ל-mutations פנימיים; API Routes נדרשים ל-public APIs.
האם Server Actions מחליפים את ה-API Routes
לא, הם משלימים. Server Actions מצטיינים ב-mutations פנימיים שנקראים מתוך הקומפוננטות של אותה אפליקציה. API Routes (Route Handlers) עדיין נחוצים במקרים הבאים: public APIs שצריכים לחשוף לאפליקציות חיצוניות, webhooks, נקודות קצה לאפליקציות מובייל, או כל מקרה שבו אתם צריכים שליטה מלאה על HTTP request/response. בפועל, אפליקציות גדולות משלבות את שניהם.
איפה כדאי לשמור את ה-Server Actions בפרויקט
הקונבנציה המקובלת היא ליצור תיקייה ייעודית כמו /app/actions/ או /src/serverActions/, ולקבץ פעולות לפי domain (createPost.ts, deleteUser.ts וכן הלאה). אפשר גם להגדיר Server Action בתוך אותו קובץ של ה-Server Component שמשתמש בה, אבל הקיבוץ בתיקייה נפרדת מקל על ניהול הרשאות, validation, ושימוש חוזר בין מספר קומפוננטות.
האם Server Actions תומכים ב-form actions ללא JavaScript
כן, וזה אחד היתרונות הגדולים שלהם. כשמעבירים Server Action ל-prop של action על אלמנט form, Next.js יוצר אוטומטית את ה-HTML form המתאים שיעבוד גם כש-JavaScript בדפדפן כבוי או עוד לא נטען (progressive enhancement). זאת אומרת שטופס יצירת פוסט, למשל, ימשיך לעבוד גם בדפדפנים ישנים או בתנאי רשת בעייתיים.
איך מאבטחים Server Actions
Server Actions זמינות מהציבור (כל מי שיש לו הקישור יכול להפעיל אותן), ולכן אבטחה היא חובה. הצעדים: לבדוק אימות (authentication) בתחילת כל פונקציה - האם המשתמש מחובר; לבדוק הרשאות (authorization) - האם המשתמש רשאי לבצע את הפעולה; לבצע validation על כל הקלט (Zod או Yup); להשתמש ב-CSRF protection מובנה של Next.js; ולא לחשוף נתונים רגישים בשגיאות שמוחזרות ללקוח.
