الآن سنبني Newsletter System احترافي يربط:
React (Frontend) → Strapi (Backend) → Database
وسأشرح السيناريو الكامل خطوة خطوة.
🧠 أولًا: الفكرة العامة
عندما يكتب المستخدم بريده:
user@example.comJavaScriptيحدث الآتي:
React Form
↓
Axios POST request
↓
Strapi API
↓
Database (newsletter subscribers)JavaScript{
"data": {
"email": "[email protected]"
}
}
Content-Type:application/jsonJavaScript🏗 الخطوة 1: إنشاء Collection في Strapi
اذهب إلى:
Content-Type BuilderJavaScriptأنشئ Collection باسم:
subscribersJavaScriptالحقول
| Field | Type |
|---|---|
| isActive | Boolean |
اختياري:
| Field | Type |
|---|---|
| source | Text |
| subscribedAt | Date |
🟢 الخطوة 2: إعطاء صلاحيات API
اذهب إلى:
Settings
→ Roles
→ PublicJavaScriptفعّل:
createJavaScriptلموديل:
subscribersJavaScriptحتى يسمح للـ frontend بالإرسال.
🌐 API الذي سيستخدمه React
Strapi يوفر تلقائيًا:
POST /api/subscribersJavaScript🟢 شكل الطلب
{
"data": {
"email": "[email protected]"
}
}JavaScriptلاحظ كلمة:
"data"JavaScriptStrapi يطلبها دائمًا.
🏗 الخطوة 3: نموذج React
NewsletterForm.jsx
import { useState } from "react";
import api from "../../api";
function NewsletterForm() {
// حالة تخزين البريد الإلكتروني
const [email, setEmail] = useState("");
// حالة عرض الرسائل للمستخدم
const [message, setMessage] = useState("");
// دالة إرسال النموذج
const handleSubmit = async (e) => {
e.preventDefault(); // منع إعادة تحميل الصفحة
try {
// إرسال البريد إلى Strapi
await api.post("/subscribers", {
data: {
email: email,
},
});
// رسالة نجاح
setMessage("تم الاشتراك بنجاح 🎉");
// تفريغ الحقل بعد الإرسال
setEmail("");
} catch (error) {
// في حال حدث خطأ (مثل تكرار البريد)
setMessage("حدث خطأ أو البريد مستخدم مسبقًا");
console.error(error);
}
};
return (
<form onSubmit={handleSubmit}>
{/* حقل البريد الإلكتروني */}
<input
type="email"
placeholder="أدخل بريدك الإلكتروني"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
{/* زر الاشتراك */}
<button type="submit">
اشترك
</button>
{/* عرض الرسالة */}
{message && <p>{message}</p>}
</form>
);
}
export default NewsletterForm;
*************************************************
import { useState } from "react";
import api from "../../api";
function NewsletterForm() {
// البريد المدخل
const [email, setEmail] = useState("");
// رسالة للمستخدم
const [message, setMessage] = useState("");
// نوع الرسالة (نجاح / خطأ)
const [type, setType] = useState("");
// حالة التحميل
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
// تنظيف البريد من الفراغات
const cleanEmail = email.trim();
// منع الإرسال إذا كان الحقل فارغ
if (!cleanEmail) {
setType("error");
setMessage("يرجى إدخال البريد الإلكتروني");
return;
}
// Regex بسيط للتحقق من صحة البريد
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(cleanEmail)) {
setType("error");
setMessage("يرجى إدخال بريد إلكتروني صحيح");
return;
}
try {
setLoading(true);
await api.post("/subscribers", {
data: {
email: cleanEmail,
},
});
// رسالة النجاح
setType("success");
setMessage("تم الاشتراك بنجاح 🎉");
// تفريغ الحقل
setEmail("");
} catch (error) {
// في حال كان البريد موجود مسبقًا
if (error.response?.data?.error?.message?.includes("unique")) {
setType("error");
setMessage("هذا البريد مشترك بالفعل");
} else {
setType("error");
setMessage("حدث خطأ أثناء الاشتراك");
}
} finally {
setLoading(false);
}
};
return (
<div className="newsletter-form">
<form onSubmit={handleSubmit} className="d-flex gap-2">
<input
type="email"
placeholder="أدخل بريدك الإلكتروني"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="form-control"
/>
<button
type="submit"
className="btn btn-primary"
disabled={loading}
>
{loading ? "جاري الإرسال..." : "اشترك"}
</button>
</form>
{/* الرسائل */}
{message && (
<p
className={`mt-2 ${
type === "success" ? "text-success" : "text-danger"
}`}
>
{message}
</p>
)}
</div>
);
}
export default NewsletterForm;JavaScript🧠 ماذا يحدث هنا؟
عند الضغط على:
submitJavaScriptReact يرسل:
api.post("/subscribers", {
data: {
email: email
}
});JavaScriptإلى:
http://localhost:1337/api/subscribersJavaScript🟢 axios instance
في ملف:
api.jsJavaScript
import axios from "axios";const api = axios.create({
baseURL: "http://localhost:1337/api"
});
export default api;JavaScript🎯 مثال الطلب الكامل
POST http://localhost:1337/api/subscribersJavaScriptbody:
{
"data": {
"email": "[email protected]"
}
}JavaScript🧠 النتيجة
Strapi يحفظ:
Database
└── subscribers
└── emailJavaScript🔒 خطوة احترافية (مهمة)
حتى لا يسجل المستخدم نفس البريد مرتين.
اذهب إلى:
email fieldJavaScriptوفعّل:
uniqueJavaScript🏆 النتيجة النهائية
✔ المستخدم يكتب الإيميل
✔ React يرسله
✔ Strapi يحفظه
✔ يظهر في لوحة الإدارة
2️⃣ منع تسجيل نفس الإيميل مرتين مع رسالة واضحة
ممتاز 👌🔥
منع تسجيل نفس البريد مرتين خطوة مهمة جدًا لأي Newsletter، وسننفذها بطريقة احترافية بحيث:
- ❌ لا يتكرر البريد في قاعدة البيانات
- ✅ تظهر رسالة واضحة للمستخدم
- ✅ لا يعتمد النظام على الـ frontend فقط (الأمان يكون في Strapi)
سنقسم الحل إلى 3 طبقات:
1️⃣ قاعدة البيانات (Strapi)
2️⃣ الـ API
3️⃣ الـ Frontend (React)
🟢 1️⃣ منع التكرار من Strapi (الأهم)
اذهب إلى:
Content-Type Builder
→ subscribers
→ email fieldJavaScriptوفعّل الخيار:
Unique fieldJavaScriptماذا يحدث الآن؟
لو حاول المستخدم إدخال نفس البريد مرة أخرى:
Strapi سيمنع الإدخال ويعيد خطأ:
{
"error": {
"message": "This attribute must be unique"
}
}JavaScriptوهذا يمنع التكرار حتى لو حاول شخص استخدام API مباشرة.
🔥 هذا مهم للأمان.
🟢 2️⃣ معالجة الخطأ في React
الآن يجب التقاط هذا الخطأ وإظهار رسالة مناسبة للمستخدم.
الكود
const handleSubmit = async (e) => {
e.preventDefault(); try {
await api.post("/subscribers", {
data: {
email: email
}
}); setMessage("تم الاشتراك بنجاح 🎉");
setEmail(""); } catch (error) {
if (error.response?.data?.error?.message?.includes("unique")) {
setMessage("هذا البريد مشترك بالفعل.");
} else {
setMessage("حدث خطأ أثناء الاشتراك.");
} }
};JavaScript🟢 3️⃣ تحسين الرسالة من Strapi (اختياري احترافي)
بدل رسالة:
This attribute must be uniqueJavaScriptيمكننا تخصيص رسالة أوضح.
الطريقة الاحترافية
ننشئ custom controller في Strapi يتحقق أولًا.
لكن غالبًا الخيار unique كافٍ.
🧠 لماذا لا نعتمد على React فقط؟
لو كتبنا:
if(emailExists){
showError();
}JavaScriptيمكن للمستخدم تجاوز هذا بسهولة عبر:
Postman
curl
API direct requestJavaScriptلذلك يجب أن يكون المنع في backend.
🏆 النتيجة النهائية
لو المستخدم أدخل بريد جديد:
تم الاشتراك بنجاح 🎉JavaScriptلو البريد موجود:
هذا البريد مشترك بالفعل.JavaScript🚀 تحسين احترافي إضافي
يمكننا إضافة:
1️⃣ التحقق من البريد قبل الإرسال
GET /subscribers?filters[email][$eq]=emailJavaScript2️⃣ منع السبام
باستخدام:
Google reCAPTCHAJavaScript3️⃣ إرسال رسالة ترحيب تلقائية
باستخدام:
Strapi Email PluginJavaScript