Newsletter Post Request

الآن سنبني Newsletter System احترافي يربط:

React (Frontend) → Strapi (Backend) → Database

وسأشرح السيناريو الكامل خطوة خطوة.

🧠 أولًا: الفكرة العامة

عندما يكتب المستخدم بريده:

user@example.com
JavaScript

يحدث الآتي:

React Form

Axios POST request

Strapi API

Database (newsletter subscribers)
JavaScript
{
  "data": {
    "email": "[email protected]"
  }
}

Content-Type:application/json
JavaScript
🏗 الخطوة 1: إنشاء Collection في Strapi

اذهب إلى:

Content-Type Builder
JavaScript

أنشئ Collection باسم:

subscribers
JavaScript
الحقول
FieldType
emailEmail
isActiveBoolean

اختياري:

FieldType
sourceText
subscribedAtDate
🟢 الخطوة 2: إعطاء صلاحيات API

اذهب إلى:

Settings
Roles
Public
JavaScript

فعّل:

create
JavaScript

لموديل:

subscribers
JavaScript

حتى يسمح للـ frontend بالإرسال.

🌐 API الذي سيستخدمه React

Strapi يوفر تلقائيًا:

POST /api/subscribers
JavaScript
🟢 شكل الطلب
{
  "data": {
    "email": "[email protected]"
  }
}
JavaScript

لاحظ كلمة:

"data"
JavaScript

Strapi يطلبها دائمًا.

🏗 الخطوة 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
🧠 ماذا يحدث هنا؟

عند الضغط على:

submit
JavaScript

React يرسل:

api.post("/subscribers", {
  data: {
    email: email
  }
});
JavaScript

إلى:

http://localhost:1337/api/subscribers
JavaScript
🟢 axios instance

في ملف:

api.js
JavaScript
import axios from "axios";const api = axios.create({
  baseURL: "http://localhost:1337/api"
});
export default api;
JavaScript
🎯 مثال الطلب الكامل
POST http://localhost:1337/api/subscribers
JavaScript

body:

{
  "data": {
    "email": "[email protected]"
  }
}
JavaScript
🧠 النتيجة

Strapi يحفظ:

Database
└── subscribers
     └── email
JavaScript
🔒 خطوة احترافية (مهمة)

حتى لا يسجل المستخدم نفس البريد مرتين.

اذهب إلى:

email field
JavaScript

وفعّل:

unique
JavaScript
🏆 النتيجة النهائية

✔ المستخدم يكتب الإيميل
✔ React يرسله
✔ Strapi يحفظه
✔ يظهر في لوحة الإدارة

2️⃣ منع تسجيل نفس الإيميل مرتين مع رسالة واضحة

ممتاز 👌🔥
منع تسجيل نفس البريد مرتين خطوة مهمة جدًا لأي Newsletter، وسننفذها بطريقة احترافية بحيث:

  • ❌ لا يتكرر البريد في قاعدة البيانات
  • ✅ تظهر رسالة واضحة للمستخدم
  • ✅ لا يعتمد النظام على الـ frontend فقط (الأمان يكون في Strapi)

سنقسم الحل إلى 3 طبقات:

1️⃣ قاعدة البيانات (Strapi)
2️⃣ الـ API
3️⃣ الـ Frontend (React)

🟢 1️⃣ منع التكرار من Strapi (الأهم)

اذهب إلى:

Content-Type Builder
subscribers
email field
JavaScript

وفعّل الخيار:

Unique field
JavaScript

ماذا يحدث الآن؟

لو حاول المستخدم إدخال نفس البريد مرة أخرى:

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 unique
JavaScript

يمكننا تخصيص رسالة أوضح.

الطريقة الاحترافية

ننشئ custom controller في Strapi يتحقق أولًا.

لكن غالبًا الخيار unique كافٍ.

🧠 لماذا لا نعتمد على React فقط؟

لو كتبنا:

if(emailExists){
  showError();
}
JavaScript

يمكن للمستخدم تجاوز هذا بسهولة عبر:

Postman
curl
API direct request
JavaScript

لذلك يجب أن يكون المنع في backend.

🏆 النتيجة النهائية

لو المستخدم أدخل بريد جديد:

تم الاشتراك بنجاح 🎉
JavaScript

لو البريد موجود:

هذا البريد مشترك بالفعل.
JavaScript
🚀 تحسين احترافي إضافي

يمكننا إضافة:

1️⃣ التحقق من البريد قبل الإرسال
GET /subscribers?filters[email][$eq]=email
JavaScript
2️⃣ منع السبام

باستخدام:

Google reCAPTCHA
JavaScript
3️⃣ إرسال رسالة ترحيب تلقائية

باستخدام:

Strapi Email Plugin
JavaScript