دليل شامل لإجراءات الخادم في Next.js 14، يغطي أفضل الممارسات لمعالجة النماذج، والتحقق من صحة البيانات، والاعتبارات الأمنية، والتقنيات المتقدمة لبناء تطبيقات الويب الحديثة.
إجراءات الخادم في Next.js 14: إتقان أفضل الممارسات لمعالجة النماذج
يقدم Next.js 14 ميزات قوية لبناء تطبيقات ويب عالية الأداء وسهلة الاستخدام. من بين هذه الميزات، تبرز إجراءات الخادم (Server Actions) كطريقة تحويلية لمعالجة عمليات إرسال النماذج وتعديلات البيانات مباشرة على الخادم. يقدم هذا الدليل نظرة شاملة على إجراءات الخادم في Next.js 14، مع التركيز على أفضل الممارسات لمعالجة النماذج، والتحقق من صحة البيانات، والأمان، والتقنيات المتقدمة. سنستكشف أمثلة عملية ونقدم رؤى قابلة للتنفيذ لمساعدتك في بناء تطبيقات ويب قوية وقابلة للتطوير.
ما هي إجراءات الخادم في Next.js؟
إجراءات الخادم هي دوال غير متزامنة تعمل على الخادم ويمكن استدعاؤها مباشرة من مكونات React. إنها تلغي الحاجة إلى مسارات API التقليدية للتعامل مع عمليات إرسال النماذج وتعديلات البيانات، مما يؤدي إلى تبسيط الكود، وتحسين الأمان، وتعزيز الأداء. إجراءات الخادم هي مكونات خادم React (RSCs)، مما يعني أنها تُنفذ على الخادم، مما يؤدي إلى تحميل أسرع للصفحات الأولية وتحسين محركات البحث.
الفوائد الرئيسية لإجراءات الخادم:
- كود مبسط: تقليل الكود المتكرر عن طريق إلغاء الحاجة إلى مسارات API منفصلة.
- أمان محسن: يقلل التنفيذ من جانب الخادم من نقاط الضعف في جانب العميل.
- أداء معزز: تنفيذ تعديلات البيانات مباشرة على الخادم لأوقات استجابة أسرع.
- تحسين محركات البحث (SEO): الاستفادة من التصيير من جانب الخادم لفهرسة أفضل في محركات البحث.
- سلامة الأنواع (Type Safety): الاستفادة من سلامة الأنواع الشاملة مع TypeScript.
إعداد مشروع Next.js 14 الخاص بك
قبل الخوض في إجراءات الخادم، تأكد من أن لديك مشروع Next.js 14 مُعدًا. إذا كنت تبدأ من الصفر، فأنشئ مشروعًا جديدًا باستخدام الأمر التالي:
npx create-next-app@latest my-next-app
تأكد من أن مشروعك يستخدم بنية دليل app
للاستفادة الكاملة من مكونات وإجراءات الخادم.
معالجة النماذج الأساسية باستخدام إجراءات الخادم
لنبدأ بمثال بسيط: نموذج يرسل بيانات لإنشاء عنصر جديد في قاعدة بيانات. سنستخدم نموذجًا بسيطًا مع حقل إدخال وزر إرسال.
مثال: إنشاء عنصر جديد
أولاً، قم بتعريف دالة إجراء خادم داخل مكون React الخاص بك. ستتولى هذه الدالة منطق إرسال النموذج على الخادم.
// app/components/CreateItemForm.tsx
'use client';
import { useState } from 'react';
async function createItem(formData: FormData) {
'use server'
const name = formData.get('name') as string;
// Simulate database interaction
console.log('Creating item:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency
console.log('Item created successfully!');
}
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
هي دالة من جانب العميل تستدعي إجراء الخادم. كما أنها تتعامل مع حالة واجهة المستخدم مثل تعطيل الزر أثناء الإرسال. - تم تعيين خاصية
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('Item name must be at least 3 characters long.');
}
// Simulate database interaction
console.log('Creating item:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency
console.log('Item created successfully!');
}
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 || 'An error occurred.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
شرح:
- تتحقق الدالة
createItem
الآن مما إذا كانname
صالحًا (3 أحرف على الأقل). - إذا فشل التحقق، يتم طرح خطأ.
- تم تحديث الدالة
handleSubmit
لالتقاط أي أخطاء يتم طرحها بواسطة إجراء الخادم وعرض رسالة خطأ للمستخدم.
استخدام مكتبات التحقق من الصحة
لسيناريوهات التحقق الأكثر تعقيدًا، فكر في استخدام مكتبات التحقق من الصحة مثل:
- Zod: مكتبة لتعريف المخططات والتحقق من صحتها تركز على TypeScript أولاً.
- Yup: أداة بناء مخططات JavaScript لتحليل القيم والتحقق من صحتها وتحويلها.
إليك مثال باستخدام Zod:
// app/utils/validation.ts
import { z } from 'zod';
export const CreateItemSchema = z.object({
name: z.string().min(3, 'Item name must be at least 3 characters long.'),
});
// 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 };
}
// Simulate database interaction
console.log('Creating item:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency
console.log('Item created successfully!');
}
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 || 'An error occurred.');
} 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('Unauthorized');
}
const name = formData.get('name') as string;
// Simulate database interaction
console.log('Creating item:', name, 'by user:', session.user?.email);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency
console.log('Item created successfully!');
}
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 || 'An error occurred.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
شرح:
- تسترد الدالة
getServerSession
معلومات جلسة المستخدم. - إذا لم يكن المستخدم مصادقًا عليه (لا توجد جلسة)، يتم طرح خطأ، مما يمنع تنفيذ إجراء الخادم.
تنقية بيانات الإدخال
قم بتنقية بيانات الإدخال لمنع هجمات البرمجة النصية عبر المواقع (XSS). تحدث هجمات XSS عند إدخال كود ضار في موقع ويب، مما قد يعرض بيانات المستخدم أو وظائف التطبيق للخطر.
استخدم مكتبات مثل DOMPurify
أو sanitize-html
لتنقية المدخلات التي يقدمها المستخدم قبل معالجتها في إجراءات الخادم الخاصة بك.
التقنيات المتقدمة
الآن بعد أن قمنا بتغطية الأساسيات، دعنا نستكشف بعض التقنيات المتقدمة لاستخدام إجراءات الخادم بفعالية.
التحديثات المتفائلة
توفر التحديثات المتفائلة تجربة مستخدم أفضل عن طريق تحديث واجهة المستخدم فورًا كما لو أن الإجراء سينجح، حتى قبل أن يؤكد الخادم ذلك. إذا فشل الإجراء على الخادم، يتم إرجاع واجهة المستخدم إلى حالتها السابقة.
// 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;
// Simulate database interaction
console.log('Updating item:', id, 'with name:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency
// Simulate failure (for demonstration purposes)
const shouldFail = Math.random() < 0.5;
if (shouldFail) {
throw new Error('Failed to update item.');
}
console.log('Item updated successfully!');
return { name }; // Return the updated 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);
// Optimistically update the UI
const newName = formData.get('name') as string;
setItemName(newName);
try {
const result = await updateItem(id, formData);
//If success then update is already reflected in UI through setItemName
} catch (error: any) {
setErrorMessage(error.message || 'An error occurred.');
// Revert the UI on error
setItemName(initialName);
} finally {
setIsSubmitting(false);
}
}
return (
Current Name: {itemName}
{errorMessage && {errorMessage}
}
);
}
شرح:
- قبل استدعاء إجراء الخادم، يتم تحديث واجهة المستخدم فورًا باسم العنصر الجديد باستخدام
setItemName
. - إذا فشل إجراء الخادم، يتم إرجاع واجهة المستخدم إلى اسم العنصر الأصلي.
إعادة التحقق من صحة البيانات
بعد أن يقوم إجراء خادم بتعديل البيانات، قد تحتاج إلى إعادة التحقق من صحة البيانات المخزنة مؤقتًا لضمان أن واجهة المستخدم تعكس أحدث التغييرات. يوفر 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;
// Simulate database interaction
console.log('Creating item:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency
console.log('Item created successfully!');
revalidatePath('/items'); // Revalidate the /items path
}
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 || 'An error occurred.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
شرح:
- تقوم الدالة
revalidatePath('/items')
بإبطال ذاكرة التخزين المؤقت لمسار/items
، مما يضمن أن الطلب التالي لهذا المسار سيجلب أحدث البيانات.
أفضل الممارسات لإجراءات الخادم
لتحقيق أقصى استفادة من إجراءات الخادم، ضع في اعتبارك أفضل الممارسات التالية:
- اجعل إجراءات الخادم صغيرة ومركزة: يجب أن تؤدي إجراءات الخادم مهمة واحدة محددة جيدًا. تجنب المنطق المعقد داخل إجراءات الخادم للحفاظ على قابلية القراءة والاختبار.
- استخدم أسماء وصفية: أعطِ إجراءات الخادم أسماء وصفية تشير بوضوح إلى الغرض منها.
- تعامل مع الأخطاء بأمان: قم بتنفيذ معالجة قوية للأخطاء لتقديم ملاحظات مفيدة للمستخدم ومنع تعطل التطبيق.
- تحقق من صحة البيانات جيدًا: قم بإجراء تحقق شامل من صحة البيانات لضمان سلامة البيانات ومنع الثغرات الأمنية.
- قم بتأمين إجراءات الخادم الخاصة بك: قم بتنفيذ آليات المصادقة والتفويض لحماية البيانات والوظائف الحساسة.
- تحسين الأداء: راقب أداء إجراءات الخادم الخاصة بك وقم بتحسينها حسب الحاجة لضمان أوقات استجابة سريعة.
- استخدم التخزين المؤقت بفعالية: استفد من آليات التخزين المؤقت في Next.js لتحسين الأداء وتقليل الحمل على قاعدة البيانات.
الأخطاء الشائعة وكيفية تجنبها
بينما توفر إجراءات الخادم مزايا عديدة، هناك بعض الأخطاء الشائعة التي يجب الانتباه إليها:
- إجراءات الخادم المعقدة للغاية: تجنب وضع الكثير من المنطق داخل إجراء خادم واحد. قسّم المهام المعقدة إلى دوال أصغر وأكثر قابلية للإدارة.
- إهمال معالجة الأخطاء: قم دائمًا بتضمين معالجة الأخطاء لالتقاط الأخطاء غير المتوقعة وتقديم ملاحظات مفيدة للمستخدم.
- تجاهل أفضل الممارسات الأمنية: اتبع أفضل الممارسات الأمنية لحماية تطبيقك من التهديدات الشائعة مثل XSS و CSRF.
- نسيان إعادة التحقق من صحة البيانات: تأكد من إعادة التحقق من صحة البيانات المخزنة مؤقتًا بعد أن يقوم إجراء خادم بتعديل البيانات للحفاظ على تحديث واجهة المستخدم.
الخاتمة
توفر إجراءات الخادم في Next.js 14 طريقة قوية وفعالة لمعالجة عمليات إرسال النماذج وتعديلات البيانات مباشرة على الخادم. باتباع أفضل الممارسات الموضحة في هذا الدليل، يمكنك بناء تطبيقات ويب قوية وآمنة وعالية الأداء. احتضن إجراءات الخادم لتبسيط الكود الخاص بك، وتعزيز الأمان، وتحسين تجربة المستخدم الإجمالية. بينما تدمج هذه المبادئ، فكر في التأثير العالمي لخياراتك التطويرية. تأكد من أن نماذجك وعمليات معالجة البيانات الخاصة بك سهلة الوصول وآمنة وسهلة الاستخدام لجماهير دولية متنوعة. هذا الالتزام بالشمولية لن يحسن فقط من قابلية استخدام تطبيقك، بل سيوسع أيضًا من نطاقه وفعاليته على نطاق عالمي.