راهنمای جامع اکشنهای سرور در Next.js 14، شامل بهترین شیوههای مدیریت فرم، اعتبارسنجی دادهها، ملاحظات امنیتی و تکنیکهای پیشرفته برای ساخت اپلیکیشنهای وب مدرن.
اکشنهای سرور در Next.js 14: تسلط بر بهترین شیوههای مدیریت فرم
Next.js 14 ویژگیهای قدرتمندی را برای ساخت اپلیکیشنهای وب با کارایی بالا و کاربرپسند معرفی میکند. در این میان، اکشنهای سرور (Server Actions) به عنوان روشی تحولآفرین برای مدیریت ارسال فرمها و تغییرات دادهها (data mutations) مستقیماً روی سرور، برجسته میشوند. این راهنما یک نمای کلی و جامع از اکشنهای سرور در Next.js 14 ارائه میدهد و بر بهترین شیوهها برای مدیریت فرم، اعتبارسنجی دادهها، امنیت و تکنیکهای پیشرفته تمرکز دارد. ما مثالهای عملی را بررسی کرده و بینشهای کاربردی را برای کمک به شما در ساخت اپلیکیشنهای وب قوی و مقیاسپذیر ارائه خواهیم داد.
اکشنهای سرور Next.js چه هستند؟
اکشنهای سرور توابع ناهمگامی هستند که روی سرور اجرا میشوند و میتوانند مستقیماً از کامپوننتهای ریاکت فراخوانی شوند. آنها نیاز به مسیرهای API سنتی برای مدیریت ارسال فرمها و تغییرات دادهها را از بین میبرند که منجر به سادهسازی کد، بهبود امنیت و افزایش عملکرد میشود. اکشنهای سرور، کامپوننتهای سرور ریاکت (RSCs) هستند، به این معنی که روی سرور اجرا میشوند و منجر به بارگذاری سریعتر صفحات اولیه و بهبود سئو میشوند.
مزایای کلیدی اکشنهای سرور:
- کد سادهتر: با حذف نیاز به مسیرهای API جداگانه، کدهای تکراری (boilerplate) را کاهش دهید.
- امنیت بهبودیافته: اجرای سمت سرور، آسیبپذیریهای سمت کلاینت را به حداقل میرساند.
- عملکرد بهتر: تغییرات دادهها را مستقیماً روی سرور اجرا کنید تا زمان پاسخدهی سریعتر شود.
- سئوی بهینهشده: از رندر سمت سرور برای ایندکس شدن بهتر توسط موتورهای جستجو بهره ببرید.
- ایمنی نوع (Type Safety): از ایمنی نوع سرتاسری با تایپاسکریپت بهرهمند شوید.
راهاندازی پروژه Next.js 14 شما
قبل از پرداختن به اکشنهای سرور، اطمینان حاصل کنید که یک پروژه Next.js 14 راهاندازی کردهاید. اگر از ابتدا شروع میکنید، با استفاده از دستور زیر یک پروژه جدید ایجاد کنید:
npx create-next-app@latest my-next-app
اطمینان حاصل کنید که پروژه شما از ساختار دایرکتوری app
استفاده میکند تا بتوانید از مزایای کامل کامپوننتهای سرور و اکشنها بهرهمند شوید.
مدیریت فرم پایه با اکشنهای سرور
بیایید با یک مثال ساده شروع کنیم: فرمی که دادهها را برای ایجاد یک آیتم جدید در پایگاه داده ارسال میکند. ما از یک فرم ساده با یک فیلد ورودی و یک دکمه ارسال استفاده خواهیم کرد.
مثال: ایجاد یک آیتم جدید
ابتدا، یک تابع اکشن سرور را در کامپوننت ریاکت خود تعریف کنید. این تابع منطق ارسال فرم را روی سرور مدیریت خواهد کرد.
// app/components/CreateItemForm.tsx
'use client';
import { useState } from 'react';
async function createItem(formData: FormData) {
'use server'
const name = formData.get('name') as string;
// شبیهسازی تعامل با پایگاه داده
console.log('در حال ایجاد آیتم:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // شبیهسازی تأخیر
console.log('آیتم با موفقیت ایجاد شد!');
}
export default function CreateItemForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
await createItem(formData);
setIsSubmitting(false);
}
return (
);
}
توضیح:
- دستورالعمل
'use client'
نشان میدهد که این یک کامپوننت کلاینت است. - تابع
createItem
با دستورالعمل'use server'
مشخص شده است که نشان میدهد یک اکشن سرور است. - تابع
handleSubmit
یک تابع سمت کلاینت است که اکشن سرور را فراخوانی میکند. همچنین وضعیتهای UI مانند غیرفعال کردن دکمه هنگام ارسال را مدیریت میکند. - ویژگی
action
عنصر<form>
به تابعhandleSubmit
تنظیم شده است. - متد
formData.get('name')
مقدار فیلد ورودی 'name' را بازیابی میکند. - دستور
await new Promise
یک عملیات پایگاه داده را شبیهسازی کرده و تأخیر ایجاد میکند.
اعتبارسنجی دادهها
اعتبارسنجی دادهها برای اطمینان از یکپارچگی دادهها و جلوگیری از آسیبپذیریهای امنیتی حیاتی است. اکشنهای سرور فرصت بسیار خوبی برای انجام اعتبارسنجی سمت سرور فراهم میکنند. این رویکرد به کاهش خطرات مرتبط با اعتبارسنجی صرفاً سمت کلاینت کمک میکند.
مثال: اعتبارسنجی دادههای ورودی
اکشن سرور createItem
را برای گنجاندن منطق اعتبارسنجی تغییر دهید.
// app/components/CreateItemForm.tsx
'use client';
import { useState } from 'react';
async function createItem(formData: FormData) {
'use server'
const name = formData.get('name') as string;
if (!name || name.length < 3) {
throw new Error('نام آیتم باید حداقل ۳ کاراکتر باشد.');
}
// شبیهسازی تعامل با پایگاه داده
console.log('در حال ایجاد آیتم:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // شبیهسازی تأخیر
console.log('آیتم با موفقیت ایجاد شد!');
}
export default function CreateItemForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
setErrorMessage(null);
try {
await createItem(formData);
} catch (error: any) {
setErrorMessage(error.message || 'خطایی رخ داد.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
توضیح:
- تابع
createItem
اکنون بررسی میکند که آیاname
معتبر است (حداقل ۳ کاراکتر طول دارد). - اگر اعتبارسنجی با شکست مواجه شود، یک خطا پرتاب میشود.
- تابع
handleSubmit
برای گرفتن هرگونه خطای پرتابشده توسط اکشن سرور و نمایش پیام خطا به کاربر بهروزرسانی شده است.
استفاده از کتابخانههای اعتبارسنجی
برای سناریوهای اعتبارسنجی پیچیدهتر، استفاده از کتابخانههایی مانند موارد زیر را در نظر بگیرید:
- Zod: یک کتابخانه تعریف اسکما و اعتبارسنجی مبتنی بر تایپاسکریپت.
- Yup: یک سازنده اسکیمای جاوااسکریپت برای تجزیه، اعتبارسنجی و تبدیل مقادیر.
در اینجا یک مثال با استفاده از Zod آورده شده است:
// app/utils/validation.ts
import { z } from 'zod';
export const CreateItemSchema = z.object({
name: z.string().min(3, 'نام آیتم باید حداقل ۳ کاراکتر باشد.'),
});
// app/components/CreateItemForm.tsx
'use client';
import { useState } from 'react';
import { CreateItemSchema } from '../utils/validation';
async function createItem(formData: FormData) {
'use server'
const name = formData.get('name') as string;
const validatedFields = CreateItemSchema.safeParse({ name });
if (!validatedFields.success) {
return { errors: validatedFields.error.flatten().fieldErrors };
}
// شبیهسازی تعامل با پایگاه داده
console.log('در حال ایجاد آیتم:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // شبیهسازی تأخیر
console.log('آیتم با موفقیت ایجاد شد!');
}
export default function CreateItemForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
setErrorMessage(null);
try {
await createItem(formData);
} catch (error: any) {
setErrorMessage(error.message || 'خطایی رخ داد.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
توضیح:
CreateItemSchema
قوانین اعتبارسنجی برای فیلدname
را با استفاده از Zod تعریف میکند.- متد
safeParse
سعی در اعتبارسنجی دادههای ورودی دارد. اگر اعتبارسنجی با شکست مواجه شود، یک شیء حاوی خطاها را برمیگرداند. - شیء
errors
حاوی اطلاعات دقیقی درباره شکستهای اعتبارسنجی است.
ملاحظات امنیتی
اکشنهای سرور با اجرای کد روی سرور امنیت را افزایش میدهند، اما همچنان پیروی از بهترین شیوههای امنیتی برای محافظت از اپلیکیشن شما در برابر تهدیدات رایج حیاتی است.
جلوگیری از جعل درخواست بین سایتی (CSRF)
حملات CSRF از اعتمادی که یک وبسایت به مرورگر کاربر دارد، سوءاستفاده میکنند. برای جلوگیری از حملات CSRF، مکانیزمهای محافظت از CSRF را پیادهسازی کنید.
Next.js هنگام استفاده از اکشنهای سرور، به طور خودکار محافظت از CSRF را مدیریت میکند. این فریمورک برای هر ارسال فرم یک توکن CSRF تولید و اعتبارسنجی میکند و اطمینان میدهد که درخواست از اپلیکیشن شما نشأت گرفته است.
مدیریت احراز هویت و مجوزدهی کاربر
اطمینان حاصل کنید که فقط کاربران مجاز میتوانند اقدامات خاصی را انجام دهند. مکانیزمهای احراز هویت و مجوزدهی را برای محافظت از دادهها و عملکردهای حساس پیادهسازی کنید.
در اینجا یک مثال با استفاده از NextAuth.js برای محافظت از یک اکشن سرور آورده شده است:
// app/components/CreateItemForm.tsx
'use client';
import { useState } from 'react';
import { getServerSession } from 'next-auth';
import { authOptions } from '../../app/api/auth/[...nextauth]/route';
async function createItem(formData: FormData) {
'use server'
const session = await getServerSession(authOptions);
if (!session) {
throw new Error('غیرمجاز');
}
const name = formData.get('name') as string;
// شبیهسازی تعامل با پایگاه داده
console.log('در حال ایجاد آیتم:', name, 'توسط کاربر:', session.user?.email);
await new Promise((resolve) => setTimeout(resolve, 1000)); // شبیهسازی تأخیر
console.log('آیتم با موفقیت ایجاد شد!');
}
export default function CreateItemForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
setErrorMessage(null);
try {
await createItem(formData);
} catch (error: any) {
setErrorMessage(error.message || 'خطایی رخ داد.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
توضیح:
- تابع
getServerSession
اطلاعات نشست (session) کاربر را بازیابی میکند. - اگر کاربر احراز هویت نشده باشد (نشستی وجود نداشته باشد)، یک خطا پرتاب میشود که از اجرای اکشن سرور جلوگیری میکند.
پاکسازی دادههای ورودی
دادههای ورودی را برای جلوگیری از حملات اسکریپتنویسی بین سایتی (XSS) پاکسازی کنید. حملات XSS زمانی رخ میدهند که کد مخرب به یک وبسایت تزریق میشود و به طور بالقوه دادههای کاربر یا عملکرد اپلیکیشن را به خطر میاندازد.
از کتابخانههایی مانند DOMPurify
یا sanitize-html
برای پاکسازی ورودیهای ارائهشده توسط کاربر قبل از پردازش آنها در اکشنهای سرور خود استفاده کنید.
تکنیکهای پیشرفته
اکنون که اصول اولیه را پوشش دادیم، بیایید برخی از تکنیکهای پیشرفته برای استفاده مؤثر از اکشنهای سرور را بررسی کنیم.
بهروزرسانیهای خوشبینانه (Optimistic Updates)
بهروزرسانیهای خوشبینانه با بهروزرسانی فوری UI، طوری که انگار عمل با موفقیت انجام خواهد شد، تجربه کاربری بهتری را فراهم میکنند، حتی قبل از اینکه سرور آن را تأیید کند. اگر عمل در سرور با شکست مواجه شود، UI به حالت قبلی خود بازگردانده میشود.
// app/components/UpdateItemForm.tsx
'use client';
import { useState } from 'react';
async function updateItem(id: string, formData: FormData) {
'use server'
const name = formData.get('name') as string;
// شبیهسازی تعامل با پایگاه داده
console.log('در حال بهروزرسانی آیتم:', id, 'با نام:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // شبیهسازی تأخیر
// شبیهسازی شکست (برای اهداف نمایشی)
const shouldFail = Math.random() < 0.5;
if (shouldFail) {
throw new Error('بهروزرسانی آیتم با شکست مواجه شد.');
}
console.log('آیتم با موفقیت بهروزرسانی شد!');
return { name }; // نام بهروزرسانی شده را برمیگرداند
}
export default function UpdateItemForm({ id, initialName }: { id: string; initialName: string }) {
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
const [itemName, setItemName] = useState(initialName);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
setErrorMessage(null);
// بهروزرسانی خوشبینانه UI
const newName = formData.get('name') as string;
setItemName(newName);
try {
const result = await updateItem(id, formData);
// در صورت موفقیت، بهروزرسانی از طریق setItemName قبلاً در UI منعکس شده است
} catch (error: any) {
setErrorMessage(error.message || 'خطایی رخ داد.');
// بازگرداندن UI در صورت خطا
setItemName(initialName);
} finally {
setIsSubmitting(false);
}
}
return (
نام فعلی: {itemName}
{errorMessage && {errorMessage}
}
);
}
توضیح:
- قبل از فراخوانی اکشن سرور، UI بلافاصله با استفاده از
setItemName
با نام آیتم جدید بهروز میشود. - اگر اکشن سرور با شکست مواجه شود، UI به نام آیتم اصلی بازگردانده میشود.
اعتبارسنجی مجدد دادهها (Revalidating Data)
پس از اینکه یک اکشن سرور دادهها را تغییر میدهد، ممکن است لازم باشد دادههای کششده را مجدداً اعتبارسنجی کنید تا اطمینان حاصل شود که UI آخرین تغییرات را منعکس میکند. Next.js چندین راه برای اعتبارسنجی مجدد دادهها فراهم میکند:
- Revalidate Path: اعتبارسنجی مجدد کش برای یک مسیر خاص.
- Revalidate Tag: اعتبارسنجی مجدد کش برای دادههای مرتبط با یک تگ خاص.
در اینجا مثالی از اعتبارسنجی مجدد یک مسیر پس از ایجاد یک آیتم جدید آورده شده است:
// app/components/CreateItemForm.tsx
'use client';
import { useState } from 'react';
import { revalidatePath } from 'next/cache';
async function createItem(formData: FormData) {
'use server'
const name = formData.get('name') as string;
// شبیهسازی تعامل با پایگاه داده
console.log('در حال ایجاد آیتم:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // شبیهسازی تأخیر
console.log('آیتم با موفقیت ایجاد شد!');
revalidatePath('/items'); // اعتبارسنجی مجدد مسیر /items
}
export default function CreateItemForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
setErrorMessage(null);
try {
await createItem(formData);
} catch (error: any) {
setErrorMessage(error.message || 'خطایی رخ داد.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
توضیح:
- تابع
revalidatePath('/items')
کش را برای مسیر/items
بیاعتبار میکند و اطمینان میدهد که درخواست بعدی به آن مسیر، آخرین دادهها را دریافت خواهد کرد.
بهترین شیوهها برای اکشنهای سرور
برای به حداکثر رساندن مزایای اکشنهای سرور، بهترین شیوههای زیر را در نظر بگیرید:
- اکشنهای سرور را کوچک و متمرکز نگه دارید: اکشنهای سرور باید یک وظیفه واحد و بهخوبی تعریفشده را انجام دهند. از منطق پیچیده در اکشنهای سرور برای حفظ خوانایی و قابلیت تستپذیری خودداری کنید.
- از نامهای توصیفی استفاده کنید: به اکشنهای سرور خود نامهای توصیفی بدهید که به وضوح هدف آنها را نشان دهد.
- خطاها را به درستی مدیریت کنید: مدیریت خطای قوی را برای ارائه بازخورد آموزنده به کاربر و جلوگیری از کرش کردن اپلیکیشن پیادهسازی کنید.
- دادهها را به طور کامل اعتبارسنجی کنید: اعتبارسنجی جامع دادهها را برای اطمینان از یکپارچگی دادهها و جلوگیری از آسیبپذیریهای امنیتی انجام دهید.
- اکشنهای سرور خود را ایمن کنید: مکانیزمهای احراز هویت و مجوزدهی را برای محافظت از دادهها و عملکردهای حساس پیادهسازی کنید.
- عملکرد را بهینه کنید: عملکرد اکشنهای سرور خود را نظارت کرده و در صورت نیاز برای اطمینان از زمان پاسخدهی سریع، آنها را بهینه کنید.
- از کش به طور مؤثر استفاده کنید: از مکانیزمهای کش Next.js برای بهبود عملکرد و کاهش بار پایگاه داده بهره ببرید.
اشتباهات رایج و نحوه اجتناب از آنها
در حالی که اکشنهای سرور مزایای متعددی را ارائه میدهند، برخی از اشتباهات رایج وجود دارد که باید از آنها آگاه باشید:
- اکشنهای سرور بیش از حد پیچیده: از قرار دادن منطق بیش از حد در یک اکشن سرور خودداری کنید. وظایف پیچیده را به توابع کوچکتر و قابل مدیریتتر تقسیم کنید.
- نادیده گرفتن مدیریت خطا: همیشه مدیریت خطا را برای گرفتن خطاهای غیرمنتظره و ارائه بازخورد مفید به کاربر در نظر بگیرید.
- نادیده گرفتن بهترین شیوههای امنیتی: از بهترین شیوههای امنیتی برای محافظت از اپلیکیشن خود در برابر تهدیدات رایج مانند XSS و CSRF پیروی کنید.
- فراموش کردن اعتبارسنجی مجدد دادهها: اطمینان حاصل کنید که پس از تغییر دادهها توسط یک اکشن سرور، دادههای کششده را برای بهروز نگه داشتن UI مجدداً اعتبارسنجی میکنید.
نتیجهگیری
اکشنهای سرور در Next.js 14 روشی قدرتمند و کارآمد برای مدیریت ارسال فرمها و تغییرات دادهها مستقیماً روی سرور ارائه میدهند. با پیروی از بهترین شیوههای ذکر شده در این راهنما، میتوانید اپلیکیشنهای وب قوی، امن و با کارایی بالا بسازید. اکشنهای سرور را برای سادهسازی کد، افزایش امنیت و بهبود تجربه کلی کاربر به کار بگیرید. همانطور که این اصول را ادغام میکنید، تأثیر جهانی انتخابهای توسعه خود را در نظر بگیرید. اطمینان حاصل کنید که فرمها و فرآیندهای مدیریت داده شما برای مخاطبان بینالمللی متنوع، در دسترس، امن و کاربرپسند هستند. این تعهد به فراگیری نه تنها قابلیت استفاده اپلیکیشن شما را بهبود میبخشد، بلکه دامنه و اثربخشی آن را در مقیاس جهانی نیز گسترش میدهد.