لماذا تُغيّر Supabase Edge Functions قواعد اللعبة في بناء API REST؟
في Otomy، نرافق المؤسسات الصغيرة والمتوسطة في فرنسا والجزائر لبناء منتجات رقمية عالية الأداء. وملاحظة واحدة تتكرّر باستمرار: المؤسّسون يُهدرون أسابيع في تجهيز البنية التحتية الخلفية بينما كان بإمكانهم اختبار منتجهم في السوق منذ اليوم الأوّل.
تعتمد Supabase Edge Functions على Deno Deploy، وتُتيح كتابة دوال serverless بلغة TypeScript تُنشر فوريًا على شبكة CDN عالمية. وعند دمجها مع قاعدة بيانات PostgreSQL ونظام المصادقة والتخزين المدمجَين في Supabase، تحصل على بنية خلفية متكاملة في ساعات معدودة.
إليك كيف نبني API REST جاهزة للإنتاج في 48 ساعة — وليست مجرّد نموذج أوّلي هشّ.
الساعات 0–4: الهندسة المعمارية ونمذجة البيانات
قبل كتابة أيّ سطر برمجي، نُرسي الأساسات.
تحديد موارد الـ API
لنأخذ حالة واقعية: API لإدارة الطلبات لمتجر إلكتروني جزائري.
الموارد المحدَّدة:
/orders— عمليات CRUD كاملة/products— قراءة فقط لعرض الكتالوج/customers— إدارة حسابات العملاء/webhooks— إشعارات نحو n8n أو Make
نمذجة قاعدة البيانات
في SQL Editor الخاصّ بـ Supabase:
CREATE TABLE products (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL,
stock INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE orders (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
customer_id UUID REFERENCES auth.users(id),
status TEXT DEFAULT 'pending' CHECK (status IN ('pending','confirmed','shipped','delivered')),
total NUMERIC(10,2) NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE order_items (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
product_id UUID REFERENCES products(id),
quantity INTEGER NOT NULL,
unit_price NUMERIC(10,2) NOT NULL
);
نصيحة Otomy: فعِّل سياسات Row Level Security (RLS) فورًا. هذا أمر غير قابل للتفاوض في بيئة الإنتاج.
الساعات 4–16: تطوير Edge Functions
تهيئة المشروع
supabase init
supabase functions new orders-api
بنية Edge Function متوافقة مع REST
إليك النمط الذي نعتمده دائمًا لمعالجة عدّة طرق HTTP داخل دالة واحدة:
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
serve(async (req) => {
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);
const url = new URL(req.url);
const id = url.searchParams.get("id");
try {
switch (req.method) {
case "GET": {
const query = id
? supabase.from("orders").select("*, order_items(*)").eq("id", id).single()
: supabase.from("orders").select("*, order_items(*)").order("created_at", { ascending: false });
const { data, error } = await query;
if (error) throw error;
return new Response(JSON.stringify(data), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
case "POST": {
const body = await req.json();
const { data, error } = await supabase.from("orders").insert(body).select().single();
if (error) throw error;
return new Response(JSON.stringify(data), {
status: 201,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
case "PATCH": {
if (!id) throw new Error("المعرّف مطلوب للتحديث");
const body = await req.json();
const { data, error } = await supabase.from("orders").update(body).eq("id", id).select().single();
if (error) throw error;
return new Response(JSON.stringify(data), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
default:
return new Response("طريقة غير مسموح بها", { status: 405, headers: corsHeaders });
}
} catch (err) {
return new Response(JSON.stringify({ error: err.message }), {
status: 400,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
});
التحقّق من صحّة البيانات
استخدم مكتبة Zod للتحقّق من المدخلات:
import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
const OrderSchema = z.object({
customer_id: z.string().uuid(),
total: z.number().positive(),
items: z.array(z.object({
product_id: z.string().uuid(),
quantity: z.number().int().positive(),
unit_price: z.number().positive(),
})).min(1),
});
الساعات 16–30: المصادقة والأمان والتكاملات
تأمين الـ API بواسطة JWT
يُدير Supabase المصادقة بشكل أصيل. استخرج رمز JWT من كلّ طلب:
const authHeader = req.headers.get("Authorization");
const { data: { user }, error } = await supabase.auth.getUser(
authHeader?.replace("Bearer ", "")
);
if (!user) return new Response("غير مصرَّح", { status: 401, headers: corsHeaders });
ربط n8n أو Make للأتمتة
بعد إنشاء كلّ طلب، أطلق سير عمل عبر webhook:
await fetch(Deno.env.get("N8N_WEBHOOK_URL")!, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ event: "order.created", data: newOrder }),
});
يمكن لهذا الـ webhook أن يُفعِّل في n8n: إرسال بريد تأكيد، إشعار Slack، تحديث المخزون، أو مزامنة مع نظام ERP.
تقييد معدّل الطلبات
خزِّن العدّادات في Supabase KV أو جدول مخصَّص للحدّ من الاستخدام المُفرط.
الساعات 30–42: الاختبارات والتوثيق
الاختبارات الآلية
deno test --allow-net --allow-env supabase/functions/tests/
اكتب اختبارات لكلّ نقطة نهاية. يمكنك الاستعانة بـ Claude AI لتوليد الحالات الحدّية بسرعة انطلاقًا من مخطّط Zod.
توثيق الـ API بمعيار OpenAPI
أنشئ مواصفة OpenAPI 3.0 وانشرها على واجهتك الأمامية عبر Vercel أو Netlify. مستهلكو الـ API سيقدّرون ذلك حقًّا.
الساعات 42–48: النشر والمراقبة
النشر في بيئة الإنتاج
supabase functions deploy orders-api --project-ref your-project-ref
هذا كلّ شيء. أصبحت الـ API REST مباشرة على الحافة (Edge)، بزمن استجابة أقلّ من 50 ميلي ثانية في معظم المناطق.
المراقبة
- Supabase Dashboard: سجلّات فورية لـ Edge Functions
- Logflare: مدمج أصلًا لتحليل السجلّات
- Uptime Robot أو Better Stack: مراقبة التوافر
قائمة التحقّق النهائية قبل الإطلاق
- ✅ تفعيل RLS على جميع الجداول
- ✅ التحقّق عبر Zod لجميع المدخلات
- ✅ مصادقة JWT مفعَّلة ومُتحقَّق منها
- ✅ إعداد CORS (استبدل
*بنطاقاتك الفعلية) - ✅ متغيّرات البيئة مخزَّنة كأسرار في Supabase
- ✅ نجاح جميع الاختبارات الآلية
- ✅ Webhooks لـ n8n/Make تعمل بشكل صحيح
- ✅ توثيق OpenAPI منشور ومتاح
ماذا تحصل عليه في 48 ساعة؟
| المكوِّن | الأداة |
|---|---|
| قاعدة البيانات | Supabase PostgreSQL |
| API serverless | Supabase Edge Functions (Deno) |
| المصادقة | Supabase Auth (JWT) |
| الأتمتة | n8n / Make عبر webhooks |
| استضافة الواجهة الأمامية | Vercel / Netlify |
| المراقبة | Logflare + Better Stack |
النتيجة: واجهة API REST متكاملة، آمنة، قابلة للتوسّع، منشورة عالميًا — دون إدارة خادم واحد.
في Otomy، هذا بالضبط هو النهج الذي نطبّقه مع المؤسسات الصغيرة والمتوسطة التي تريد إطلاق منتج رقمي بسرعة، سواء في فرنسا أو الجزائر. سرعة التنفيذ ليست عدوّة الجودة — بشرط اختيار الأدوات المناسبة.