بناء تطبيق Node.js مع Supabase: ربط قاعدة البيانات وإدارة المصادقة
مقدمة عن Supabase وخدماتها للتطبيقات الخلفية
الفصل الثاني: مقدمة عن Supabase وخدماتها للتطبيقات الخلفية
في عالم تطوير التطبيقات الحديثة، لم يعد بناء البنية التحتية الخلفية (Backend) من الصفر خيارًا عمليًا للفرق التي تسعى للسرعة والكفاءة. هنا يأتي دور Supabase، ليس مجرد بديل مفتوح المصدر لـ Firebase، بل هو منصة متكاملة تعتمد على أدوات قوية ومألوفة للمطورين المحترفين. في جوهرها، Supabase هي طبقة تجريد (Abstraction Layer) توفر واجهة برمجة تطبيقات (API) تلقائية وآمنة فوق نظام قواعد البيانات العلائقية PostgreSQL، مصحوبة بمجموعة من الخدمات الأساسية التي تغطي احتياجات التطبيق الحديث.
الفلسفة المعمارية: PostgreSQL كحجر الزاوية
يفهم المطورون المحترفون أن اختيار قاعدة البيانات هو قرار استراتيجي طويل الأمد. تختار Supabase PostgreSQL لسبب جوهري: القوة والمرونة والموثوقية. بدلاً من ابتكار لغة استعلام جديدة، تقدم Supabase واجهة RESTful و Realtime API تعمل مباشرة على مخطط قاعدة البيانات الخاص بك. هذا يعني أنك تحصل على كل قوة SQL، والروابط (Joins)، والمؤشرات (Indexes)، والوظائف المخزنة (Stored Procedures)، مع سهولة استخدام تشبه NoSQL. هذه الطبقة (API Layer) تُولد تلقائيًا بناءً على مخطط قاعدة البيانات (Database Schema)، مما يوفر وقتًا هائلًا في كتابة نقاط النهاية (Endpoints) اليدوية مع الحفاظ على التحكم الدقيق في العلاقات والسلامة المرجعية (Referential Integrity).
الخدمات الأساسية: أكثر من مجرد قاعدة بيانات
لفهم كيفية دمج Supabase في تطبيق Node.js، يجب تفكيك خدماتها الرئيسية وكيفية تفاعلها مع الكود الخلفي الخاص بك.
- قاعدة البيانات (Database): جوهر المنصة. كل جدول تنشئه يحصل فورًا على واجهة RESTful و Realtime API آمنة. يتم فرض السياسات الأمنية (Policies) على مستوى قاعدة البيانات باستخدام نظام الصلاحيات المدمج في PostgreSQL (Row Level Security - RLS)، مما يجعل الأمان مستقلًا عن منطق التطبيق.
-
المصادقة (Authentication): نظام مصادقة كامل يدير المستخدمين والجلسات. يدعم تسجيل الدخول عبر البريد الإلكتروني/كلمة المرور، OAuth (مثل Google, GitHub)، والمصادقة السحرية (Magic Links). يتم تخزين بيانات المستخدمين في جدول
auth.usersمعزول، ويمكن ربطه بجداولك العامة عبر مفاتيح أجنبية (Foreign Keys). - التخزين (Storage): خدمة لتخزين وإدارة الملفات (صور، فيديوهات، مستندات) مع سياسات وصول دقيقة. مثالية عندما تحتاج إلى تخزين ملفات مرتبطة بسجلات في قاعدة البيانات.
- الوقت الحقيقي (Realtime): تتيح الاشتراك (Subscribe) في تحديثات قاعدة البيانات الفورية عبر WebSockets. هذه ليست مجرد "قنوات" عامة؛ يمكنك الاشتراك في تغييرات صفوف محددة بناءً على استعلامات معقدة.
- الوظائف (Edge Functions): ربما الأداة الأقوى للمطور الخلفي. تسمح لك بتنفيذ دوال Node.js Typescript على الحافة (Edge) للتعامل مع المنطق المعقد الذي لا يمكن تنفيذه عبر SQL أو يحتاج إلى اتصال بخدمات خارجية. تعتبر مكملة، وليست بديلة، لوظائف قاعدة البيانات.
التكامل مع Node.js: نمط التفاعل الاحترافي
عند العمل مع Node.js، يمكنك التفاعل مع Supabase بطريقتين رئيسيتين: إما من جانب الخادم (Server-Side) باستخدام مفتاح الخدمة (Service Role Key) للعمليات التي تتطلب صلاحيات إدارية، أو من جانب العميل (Client-Side) عبر مكتبة JavaScript مع رمز وصول المستخدم (JWT). سنركز هنا على النمط الاحترافي للتكامل من جانب الخادم.
// تهيئة عميل Supabase في تطبيق Node.js (جانب الخادم)
const { createClient } = require('@supabase/supabase-js');
require('dotenv').config(); // لتحميل المتغيرات البيئية
// استخدم مفتاح الخدمة (Service Role Key) للعمليات الإدارية الآمنة
// تحذير: لا تعرض هذا المفتاح أبدًا في كود العميل (Frontend).
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
const supabase = createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false, // لا حاجة لتحديث الرمز على الخادم
persistSession: false // لا حاجة لحفظ الجلسة على الخادم
},
// إعدادات عالمية للاستعلامات
global: {
headers: { 'x-application-name': 'my-nodejs-backend' },
},
});
/**
* دالة مساعدة لتنفيذ استعلام معقد مع معالجة الأخطاء.
* توضح قوة الجمع بين منطق Node.js وقوة PostgreSQL عبر Supabase.
*/
async function getUsersWithOrders(status = 'pending') {
try {
// 1. استخدم supabase.rpc() لاستدعاء دالة مخزنة في PostgreSQL إذا كان المنطق معقدًا جدًا لـ Supabase JS.
// 2. بدلاً من ذلك، استخدم supabase.from() للاستعلام المباشر مع الانضمامات (Joins).
const { data, error, status: queryStatus } = await supabase
.from('users')
.select(`
id,
email,
full_name,
orders (
id,
amount,
created_at
)
`)
.eq('orders.status', status) // تصفية على علاقة فرعية (Sub-relation)
.order('created_at', { foreignTable: 'orders', ascending: false });
if (error) {
// معالجة أخطاء Supabase المحددة (مثل انتهاكات RLS)
throw new Error(`Supabase Query Error [${queryStatus}]: ${error.message}`);
}
// data هنا تحتوي على مصفوفة من المستخدمين مع أوامرهم المضمنة (Nested)
return { success: true, data };
} catch (err) {
// سجل الخطأ في نظام المراقبة (Logging/Monitoring)
console.error('Failed to fetch users with orders:', err);
return { success: false, error: err.message };
}
}
ANON_KEY و SERVICE_ROLE_KEY حاسم. المفتاح المجهول (ANON_KEY) يحترم سياسات أمان الصفوف (RLS)، بينما مفتاح دور الخدمة (SERVICE_ROLE_KEY) يتجاوزها جميعًا. استخدم دور الخدمة فقط في بيئة خلفية آمنة (Server-Side) لأتمتة المهام أو إدارة البيانات. استخدامه في العميل (Frontend) يعرض كامل قاعدة البيانات للخطر.
الكود أعلاه يوضح النهج الاحترافي: التهيئة مع إعدادات مصممة لبيئة الخادم، واستخدام انتقاء (Select) متداخل للاستفادة من علاقات PostgreSQL، ومعالجة الأخطاء بشكل دقيق. لاحظ كيف أن الاستعلام يستخدم بناء الجملة
جاري تحميل التقييمات...