For freelance projects, Supabase is the closest thing to a cheat code I've found. You get a real Postgres database, authentication, file storage, edge functions, and realtime, all behind one client SDK, all on a free tier that covers most early-stage apps. The combination eliminates the parts of backend work that don't directly serve the client (server provisioning, auth flows, file upload plumbing, deployment pipelines) and lets me ship features that actually justify my invoice. Row Level Security is the underrated headline feature: it pushes access control into the database itself, which means a tiny frontend-heavy team can ship a multi-tenant app safely. I don't use Supabase for everything; some projects genuinely need different tools. But for the 90% of freelance work that looks like "CRUD with auth, file uploads, and a few realtime touches," nothing else gets me from kickoff to launch faster.
When you're freelancing, every hour you spend on backend setup is an hour you're not building features the client actually cares about. Clients don't pay you to provision a database server. They don't pay you to write a password reset flow from scratch. They don't pay you to think about S3 IAM policies or set up nightly backups. They pay you for the thing that solves their problem.
After years of cobbling together my own stacks (Express on Heroku with Postgres on RDS, then Firebase, then Hasura, then back to Postgres on Render), I needed a backend that:
Supabase checks every box. Two years in, I've never wanted to leave.
A real relational database with foreign keys, joins, constraints, views, materialized views, full-text search, JSON columns, and every other Postgres feature that's been battle-tested over thirty years. This matters more than people give it credit for. A "modern" document database lets you ship the first feature faster, then punishes you when your data model evolves. Postgres lets you ship the first feature in a perfectly survivable way, and rewards you when the data model grows up.
Row Level Security (RLS) is the killer feature. You write SQL policies that say "users can only see their own rows," and Postgres enforces it for every query, no matter where it comes from: your React app, your edge function, a SQL console session. That single feature eliminates an entire category of authorization bugs that plague hand-rolled APIs.
Email/password, OAuth (Google, GitHub, Apple, Twitter, Discord, dozens more), magic links, SMS OTP, phone auth, anonymous sessions, all built in. The client SDKs handle session management, refresh tokens, and persistence automatically. A typical login form in my projects is fifteen lines of code, and it's been the same fifteen lines for two years.
The piece I value most is the JWT integration with RLS. Every authenticated request automatically carries the user's identity into the database, which means RLS policies can reference auth.uid() and the right user always gets the right rows.
File uploads with bucket-level access control. Perfect for user avatars, project images, document uploads, and product photos. You can mark a bucket public or private, write RLS-style policies for object access, and the SDK gives you signed URLs with expiry built in. I've shipped projects with gigabytes of user-uploaded files and never had to think about S3 once.
When I need server-side logic (webhook handlers, third-party API calls, scheduled jobs, anything that shouldn't run in the browser), Edge Functions run on Deno at the edge. No server to provision, no Dockerfile to write, no autoscaling to configure. I write a function, run supabase functions deploy, and it's live in seconds.
The Deno runtime means I get TypeScript natively, modern ESM imports, and a much smaller cold-start surface than the equivalent Node Lambda would have.
For apps that need live updates (chat, dashboards, collaborative tools, multi-user forms), Supabase Realtime broadcasts database changes over WebSockets. You subscribe to a table or a query, and your React state updates the instant the row changes anywhere in the world. The same RLS policies apply, so users only get notified about rows they're allowed to see.
In the last year Supabase added native cron scheduling (via pg_cron) and vector search (via pg_vector). I now keep all my scheduled jobs and most of my AI embeddings in the same Postgres instance as the rest of the app data. Fewer moving parts, simpler permissions, lower bill.
The path from "kickoff call" to "client clicking a working app" usually looks like this:
supabase db diff to capture migrations once the schema settles.supabase gen types typescript. The generated types make end-to-end type safety work without a single hand-written model.@supabase/supabase-js from the React app. One client instance, imported wherever I need data.A simple authenticated fetch ends up looking like this:
const { data: posts } = useQuery({
queryKey: ['posts'],
queryFn: async () => {
const { data, error } = await supabase
.from('posts')
.select('id, title, body, created_at, profiles(name)')
.order('created_at', { ascending: false });
if (error) throw error;
return data;
},
});
Notice what's missing: no auth header to attach manually, no permission filtering in JavaScript, no error mapping. RLS handles the permission. The session token is attached automatically. TanStack Query handles caching, retries, and stale-while-revalidate behavior.
Here's a policy for a multi-tenant SaaS where each user can only read posts they own:
alter table posts enable row level security;
create policy "users can read their own posts"
on posts for select
using (auth.uid() = user_id);
create policy "users can insert their own posts"
on posts for insert
with check (auth.uid() = user_id);
That's it. The frontend doesn't need a "where user_id = current_user" filter. The backend doesn't need a middleware checking ownership. The database enforces it for every read and write, end of story.
For multi-org SaaS the policies get slightly more involved (you usually join through a memberships table), but the pattern stays the same: the rule lives once, in the database, and no client can bypass it.
Most freelance projects fit comfortably in the free tier: 500MB database, 1GB storage, 50,000 monthly active users, 5GB egress. That's enough for an early-stage SaaS, an MVP, an internal tool, or a portfolio-style site with user uploads.
When projects outgrow the free tier, the Pro plan is $25/month, which includes 8GB of database, 100GB of storage, 100,000 MAU, and daily backups. For comparison, the equivalent self-hosted setup on AWS (RDS db.t4g.small + S3 + Cognito + Lambda) runs around $80 - 120/month before you've written a single migration or done a single deploy.
I almost always include a line item in proposals: "Hosting and backend: approximately $25/month, billed to your account directly." Clients appreciate the transparency, and the number is small enough that no one balks.
It's not a universal answer. There are projects where I reach for something else:
For everything else (and that's 90% of freelance work), Supabase is the answer.
Three things took me longer than they should have to learn:
1. Enable RLS the moment you create a table. Always. Even on prototypes. Even on internal tools. The cost is one minute; the cost of forgetting is a data breach.
2. Generate types and use them. Don't hand-write your data models. The Supabase CLI's type generation makes your IDE catch schema mismatches at compile time, which is worth more than any documentation.
3. Use migrations, not the SQL editor. It's tempting to make schema changes in the dashboard. Don't. Use supabase db diff to capture them as migrations, commit them to git, and apply them per environment. Future-you, trying to spin up a new environment, will thank present-you.
Supabase is the rare tool that makes you faster without making you dumber. It encodes good defaults (Postgres, RLS, edge runtime, generated types) without forcing you into them. That's the bar I now hold every other backend tool to.
لمشاريع العمل الحرّ، Supabase أقرب شيء وجدته إلى كود غشّ. تحصل على قاعدة بيانات Postgres حقيقيّة، ومصادقة، وتخزين ملفّات، وEdge Functions، وRealtime، كلّها خلف SDK واحد، كلّها ضمن باقة مجانيّة تغطّي معظم التطبيقات في مراحلها الأولى. هذا التكامل يلغي الأجزاء من عمل الباك إند التي لا تخدم العميل مباشرة (تشغيل خوادم، تدفّقات مصادقة، توصيل رفع الملفّات، خطوط نشر) ويتيح لي شحن مزايا تستحقّ فاتورتي فعلاً. Row Level Security هي المزيّة الكبرى المُهمَلة: تدفع التحكّم بالوصول إلى داخل قاعدة البيانات نفسها، مما يعني أن فريقاً صغيراً يعتمد على الواجهة الأماميّة يستطيع شحن تطبيق متعدّد المستأجرين بأمان. لا أستخدم Supabase لكل شيء؛ بعض المشاريع تحتاج أدوات أخرى. لكن للـ 90% من العمل الحرّ التي تبدو كـ "CRUD مع مصادقة ورفع ملفّات ولمسات Realtime"، لا شيء يوصلني من الانطلاق إلى الإطلاق أسرع.
حين تعمل كمستقلّ، كل ساعة تقضيها في إعداد الباك إند ساعة لا تبني فيها المزايا التي يهتمّ بها العميل. العميل لا يدفع لك لتشغيل خادم قاعدة بيانات. لا يدفع لك لتكتب تدفّق إعادة تعيين كلمة سرّ من الصفر. لا يدفع لك لتفكّر في سياسات IAM على S3 أو لإعداد نسخ احتياطيّة ليليّة. يدفع لك مقابل الشيء الذي يحلّ مشكلته.
بعد سنوات من تركيب ستاكات بنفسي (Express على Heroku مع Postgres على RDS، ثم Firebase، ثم Hasura، ثم العودة لـ Postgres على Render)، احتجت باك إند:
Supabase يحقّق كل شرط. عامان فيه ولم أرغب يوماً بالرحيل.
قاعدة بيانات علائقيّة حقيقيّة مع مفاتيح خارجيّة وJoins وقيود وViews وViews ماديّة وبحث نصّيّ كامل وأعمدة JSON وكلّ ما اختبرته Postgres على مدى ثلاثين سنة. هذا أهمّ مما يعطيه الناس قدره. قاعدة بيانات وثائقيّة "حديثة" تتيح شحن الميزة الأولى أسرع، ثم تعاقبك حين يتطوّر نموذج البيانات. Postgres يتيح شحن الميزة الأولى بطريقة قابلة للنجاة تماماً، ويكافئك حين ينضج نموذج البيانات.
Row Level Security هي القاتلة. تكتب سياسات SQL تقول "المستخدمون يرون فقط صفوفهم"، وPostgres يفرضها لكل استعلام بصرف النظر عن مصدره: تطبيقك React، أو Edge Function، أو جلسة Console. هذه المزيّة وحدها تلغي فئة كاملة من ثغرات التفويض التي تنخر الواجهات اليدويّة.
بريد إلكتروني/كلمة سرّ، OAuth (Google، GitHub، Apple، Twitter، Discord، عشرات أخرى)، روابط سحريّة، SMS OTP، مصادقة الهاتف، جلسات مجهولة، كلّها جاهزة. SDKs العميل تتولّى إدارة الجلسات والـ Refresh Tokens والتخزين تلقائياً. نموذج تسجيل دخول نموذجيّ في مشاريعي خمسة عشر سطراً من الكود، وكان نفسه منذ عامين.
الجزء الذي أقدّره أكثر هو تكامل JWT مع RLS. كل طلب مصدَّق يحمل تلقائياً هويّة المستخدم إلى قاعدة البيانات، ما يعني أن سياسات RLS تستطيع الإشارة إلى auth.uid() ويصل دائماً المستخدم الصحيح للصفوف الصحيحة.
رفع ملفّات مع تحكّم بالوصول على مستوى الـ Bucket. مثاليّ لصور الحساب وصور المشاريع وتحميل المستندات وصور المنتجات. تستطيع تعليم Bucket عامّاً أو خاصّاً، وكتابة سياسات بنمط RLS للوصول إلى الكائنات، وSDK يعطيك Signed URLs مع انتهاء صلاحيّة جاهزاً. شحنت مشاريع بجيغابايتات من ملفّات المستخدمين ولم أحتج للتفكير في S3 ولا مرّة.
حين أحتاج منطقاً جانب الخادم (معالجة Webhooks، استدعاء API طرف ثالث، وظائف مجدولة، أيّ شيء لا يجب تشغيله في المتصفّح)، Edge Functions تعمل على Deno على الحافّة. لا خادم لتشغّله، لا Dockerfile لتكتبه، لا Autoscaling لتضبطه. تكتب الدالّة، تشغّل supabase functions deploy، وتصير حيّة خلال ثوانٍ.
زمن تشغيل Deno يعني TypeScript بشكل أصيل، واستيرادات ESM حديثة، ومساحة بداية باردة أصغر بكثير من Lambda Node المكافئ.
للتطبيقات التي تحتاج تحديثات حيّة (دردشة، لوحات تحكّم، أدوات تعاونيّة، نماذج متعدّدة المستخدمين) Supabase Realtime يبثّ تغييرات قاعدة البيانات عبر WebSockets. تشترك في جدول أو استعلام، وحالة React عندك تتحدّث لحظة تغيّر الصفّ في أيّ مكان في العالم. نفس سياسات RLS تنطبق، فيُشعَر المستخدمون فقط بالصفوف المسموح لهم رؤيتها.
في السنة الأخيرة أضافت Supabase جدولة Cron أصيلة (عبر pg_cron) وبحث Vector (عبر pg_vector). صرت أحفظ كل وظائفي المجدولة ومعظم تضمينات الذكاء الاصطناعي في نفس مثيل Postgres مع باقي بيانات التطبيق. أجزاء متحرّكة أقلّ، أذونات أبسط، فاتورة أقلّ.
المسار من "مكالمة الانطلاق" إلى "العميل يضغط على تطبيق يعمل" يبدو عادة هكذا:
supabase db diff لالتقاط Migrations حين يستقرّ المخطّط.supabase gen types typescript. الأنواع المولَّدة تجعل أمان الأنواع طرف-إلى-طرف يعمل بلا نموذج بيانات يدويّ واحد.@supabase/supabase-js من تطبيق React. مثيل عميل واحد، يُستورَد حيث أحتاج بيانات.استدعاء مصدَّق بسيط ينتهي إلى الشكل:
const { data: posts } = useQuery({
queryKey: ['posts'],
queryFn: async () => {
const { data, error } = await supabase
.from('posts')
.select('id, title, body, created_at, profiles(name)')
.order('created_at', { ascending: false });
if (error) throw error;
return data;
},
});
لاحظ الغائب: لا Header مصادقة يدويّ، لا تصفية أذونات في JavaScript، لا تحويل أخطاء. RLS يتولّى الإذن. توكن الجلسة يُربط تلقائياً. TanStack Query يتولّى الكاشينغ وإعادة المحاولات وسلوك Stale-While-Revalidate.
هذه سياسة لـ SaaS متعدّد المستأجرين حيث يقرأ كل مستخدم منشوراته فقط:
alter table posts enable row level security;
create policy "users can read their own posts"
on posts for select
using (auth.uid() = user_id);
create policy "users can insert their own posts"
on posts for insert
with check (auth.uid() = user_id);
هذا كل شيء. الواجهة الأماميّة لا تحتاج فلتر "where user_id = current_user". الباك إند لا يحتاج Middleware يفحص الملكيّة. قاعدة البيانات تفرضها لكلّ قراءة وكتابة، انتهى.
لـ SaaS متعدّد المنظّمات تصبح السياسات أكثر تعقيداً قليلاً (عادة تربط عبر جدول عضويّات)، لكنّ النمط يبقى نفسه: القاعدة تعيش مرّة في قاعدة البيانات، ولا عميل يتجاوزها.
معظم مشاريع العمل الحرّ تناسب الباقة المجانيّة بسهولة: 500 ميغابايت قاعدة بيانات، 1 جيغابايت تخزين، 50,000 مستخدم نشط شهرياً، 5 جيغابايت Egress. هذا يكفي لـ SaaS في مرحلة مبكّرة، أو MVP، أو أداة داخليّة، أو موقع بأسلوب بورتفوليو مع رفع مستخدمين.
حين يتجاوز المشروع الباقة المجانيّة، خطّة Pro بـ 25 دولاراً شهرياً، وتشمل 8 جيغابايت قاعدة بيانات، 100 جيغابايت تخزين، 100,000 مستخدم نشط شهرياً، ونسخ احتياطيّة يوميّة. للمقارنة، الإعداد المكافئ مستضافاً ذاتياً على AWS (RDS db.t4g.small + S3 + Cognito + Lambda) يكلّف حوالي 80 إلى 120 دولاراً شهرياً قبل أن تكتب Migration واحداً أو تنشر مرّة.
أضمّن دائماً تقريباً سطراً في العروض: "الاستضافة والباك إند: حوالي 25 دولاراً شهرياً، تُفوتَر مباشرة على حسابك". العملاء يقدّرون الشفافيّة، والرقم صغير بما يكفي ليلا يتردّد أحد.
ليس إجابة كونيّة. ثمّة مشاريع ألجأ فيها إلى غيره:
لكلّ ما عداه (و90% من العمل الحرّ) Supabase هو الجواب.
ثلاثة أمور أخذت منّي وقتاً أطول مما يجب لأتعلّمها:
1. فعّل RLS لحظة إنشاء الجدول. دائماً. حتى في النماذج الأوّليّة. حتى في الأدوات الداخليّة. الكلفة دقيقة واحدة؛ كلفة النسيان اختراق بيانات.
2. ولّد الأنواع واستخدمها. لا تكتب نماذج بياناتك يدوياً. توليد أنواع CLI الخاصّ بـ Supabase يجعل IDE يلتقط عدم تطابق المخطّط وقت الترجمة، وهذا يساوي أكثر من أيّ توثيق.
3. استخدم Migrations لا محرّر SQL. يغريك إجراء تغييرات المخطّط في اللوحة. لا تفعل. استخدم supabase db diff لالتقاطها كـ Migrations، التزمها في git، وطبّقها لكلّ بيئة. أنت المستقبليّ، الذي يحاول إطلاق بيئة جديدة، سيشكرك أنت الحاليّ.
Supabase أداة نادرة تجعلك أسرع بلا أن تجعلك أغبى. تشفّر إعدادات افتراضيّة جيّدة (Postgres، RLS، Edge Runtime، أنواع مولَّدة) بلا فرضها عليك. هذا المعيار الذي صرت أرفع كلّ أداة باك إند أخرى إليه.
هل أحتاج Supabase CLI أم تكفي اللوحة؟ ابدأ باللوحة، لكن انقل بسرعة إلى CLI. supabase db diff ينقذك مرّات لا تحصى حين تنشر إلى Production.
كيف أتعامل مع Backups؟ Supabase تقدّم نسخاً يوميّة على Pro. لمشاريع حسّاسة، أضف pg_dump ليليّاً إلى S3 مستقلّ. النسخة المنفصلة دفاعك ضدّ أيّ كارثة على مستوى المزوّد.
هل RLS تكفي بدل Backend منفصل؟ لـ 90% من تطبيقات CRUD نعم. للمنطق المعقّد (دفعات معاملات، استدعاءات طرف ثالث ذات حالة) تحتاج Edge Functions.
كيف أهاجر من Firebase؟ صدّر بيانات Firestore إلى JSON، طبّع إلى جداول علائقيّة في Postgres، وأعد بناء سياسات Auth في RLS. اختبر RLS قبل تشغيل الترحيل.
هل Realtime يتوسّع؟ لمئات الاتصالات المتزامنة، نعم. لعشرات الآلاف، تحتاج التفكير في تصميم القنوات والـ Channels. عُد لوثائق Supabase Realtime للحدود الحاليّة.
ما البديل لو Supabase توقّفت؟ لأنها مفتوحة المصدر تستطيع استضافتها ذاتياً، أو الانتقال لـ Postgres مُدار (Neon، RDS) وإعادة تنفيذ Auth بـ Lucia أو Clerk. التعرّض أصغر مما يبدو.