כשבונים אפליקציית ווב, לעיתים קרובות צריך להקים במהירות backend עם אימות, מסד נתונים ועדכונים בזמן אמת. Supabase, פלטפורמת קוד פתוח הבנויה על PostgreSQL, מכסה את הצרכים האלה במערכת אקולוגית אחת. הגדרנו את Supabase לעשרות פרויקטים—החל מ-MVP של סטארטאפים ועד שירותים בעומס גבוה עם מיליוני בקשות. במאמר זה, נעבור על הגדרה אמיתית: סכמה, RLS, Edge Functions, ואינטגרציה עם React/Next.js. נשתף טעויות אופייניות שכדאי להימנע מהן ומקרה בוחן משירות עדכוני חדשות עם עדכונים בזמן אמת.
למה Supabase על פני Firebase?
| קריטריון | Supabase | Firebase |
|---|---|---|
| רישיון | קוד פתוח (Apache 2.0) | קנייני |
| מסד נתונים | PostgreSQL (רלציוני, SQL) | Firestore (NoSQL, מסמכים) |
| זמן אמת | WebSockets, PG notify | WebSockets עם מגבלות |
| אימות | מובנה, OAuth, Magic Link | Firebase Auth |
| אחסון | תואם S3 (MinIO) | Firebase Storage |
| Edge Functions | Deno (TypeScript) | Cloud Functions (Node.js) |
| נעילת ספק | לא (אפשר אירוח עצמי) | גבוהה |
Supabase נותן לך שליטה מלאה: אפשר לפרוס אותו על שרתים משלך או להשתמש בגרסת הענן. זה קריטי לפרויקטים עם דרישות שהות נתונים.
יצירת פרויקט
דרך dashboard.supabase.com—בחר אזור, הגדר סיסמת מסד נתונים. לאירוח עצמי:
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .env
# Отредактировать .env: POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY
docker compose up -d הגדרת לקוח ויצירת טיפוסים
import { createClient } from '@supabase/supabase-js'
import type { Database } from './database.types'
export const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// Для серверных операций — service role key
export const supabaseAdmin = createClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
)הרץ git clone --depth 1 https://github.com/supabase/supabase cd supabase/docker cp .env.example .env # Отредактировать .env: POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY docker compose up -d להשלמה אוטומטית ובטיחות טיפוסים.
סכמה ואבטחת רמת שורה
-- Таблица профилей (расширение auth.users)
CREATE TABLE profiles (
id uuid PRIMARY KEY REFERENCES auth.users ON DELETE CASCADE,
username text UNIQUE,
avatar_url text,
bio text,
updated_at timestamptz DEFAULT now()
);
-- RLS — каждый видит только своё
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "profiles_select_own" ON profiles FOR SELECT USING (auth.uid() = id);
CREATE POLICY "profiles_update_own" ON profiles FOR UPDATE USING (auth.uid() = id);
-- Публичные профили — читают все
CREATE POLICY "profiles_select_public" ON profiles FOR SELECT USING (true);
-- Таблица постов с RLS
CREATE TABLE posts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid REFERENCES auth.users NOT NULL,
title text NOT NULL,
content text,
is_public boolean DEFAULT false,
created_at timestamptz DEFAULT now()
);
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "posts_select_public_or_own" ON posts FOR SELECT USING (
is_public = true OR auth.uid() = user_id
);
CREATE POLICY "posts_insert_own" ON posts FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "posts_update_own" ON posts FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "posts_delete_own" ON posts FOR DELETE USING (auth.uid() = user_id);
איך להגדיר אימות ופעולות CRUD
// Регистрация
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'password',
options: {
data: {
username: 'john_doe'
}
}
})
// OAuth (Google, GitHub, etc.)
await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`
}
})
// Вставка с возвратом
const { data: post, error } = await supabase
.from('posts')
.insert({ title, content, user_id: user.id })
.select()
.single()
// Запрос с фильтрами и JOIN
const { data: posts } = await supabase
.from('posts')
.select(`
id,
title,
created_at,
profiles (
username,
avatar_url
),
tags (
name
)
`)
.eq('is_public', true)
.order('created_at', { ascending: false })
.range(0, 23)
// Полнотекстовый поиск (PostgreSQL tsquery)
const { data } = await supabase
.from('posts')
.select('*')
.textSearch('content', query, { config: 'russian', type: 'websearch' }) איך להגדיר מנויי זמן אמת ו-Edge Functions
// Подписка на новые записи
const channel = supabase
.channel('posts-feed')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'posts', filter: 'is_public=eq.true' }, (payload) => {
setPosts(prev => [payload.new as Post, ...prev])
})
.subscribe()
// Edge Function: send-notification
import { serve } from 'https://deno.land/[email protected]/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve(async (req) => {
const { userId, message } = await req.json()
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
await supabase
.from('notifications')
.insert({ user_id: userId, message })
return new Response(JSON.stringify({ ok: true }), {
headers: { 'Content-Type': 'application/json' }
})
}) טעויות אופייניות בהגדרת Supabase
- שכפול מדיניות RLS. מפתחים רבים יוצרים מספר מדיניות עבור טבלה אחת, מה שמוביל להתנהגות לא מוגדרת. מדיניות אחת לכל פעולה עם תנאים נכונים מספיקה.
- חוסר באינדקסים. ללא אינדקסים, שאילתות שמסננות לפי
import { createClient } from '@supabase/supabase-js' import type { Database } from './database.types' export const supabase = createClient<Database>( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ) // Для серверных операций — service role key export const supabaseAdmin = createClient<Database>( process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY! )אוnpx supabase gen types typescript --project-id your-project-id > src/types/database.types.tsיכולות לרוץ לאט פי עשרות. הוסף אינדקסים על עמודות בשימוש תדיר. - התעלמות מחיבורי מסד נתונים. בסביבות serverless, כל יצירת לקוח Supabase חדש פותחת חיבור חדש. השתמש ב-connection pooling או ב-client יחיד.
איך אנחנו מגדירים Supabase: תהליך שלב-אחר-שלב
- ניתוח ועיצוב סכמה—אנחנו דנים בדרישות, מתכננים טבלאות, אינדקסים ויחסים.
- הגדרת אימות—אנחנו מגדירים ספקי OAuth ויוצרים מדיניות RLS.
- אינטגרציית Frontend—אנחנו מגדירים את ה-SDK של הלקוח ומייצרים טיפוסי TypeScript.
- פריסה וניטור—אנחנו פורסים לענן או לאירוח עצמי, מגדירים גיבויים ומתעדים את כל ההחלטות.
מה כלול בהגדרה סוהר
- סכמת מסד נתונים מעוצבת עם אינדקסים ומיגרציות.
- מדיניות RLS מוכנות להגנת נתונים.
- אינטגרציית אימות (אימייל/סיסמה, OAuth, Magic Link).
- קוד לקוח עם טיפוסי TypeScript (React, Vue, Next.js).
- מנויי זמן אמת ו-Edge Functions למקרי השימוש שלך.
- תיעוד API וגישה.
- הדרכת צוות על Supabase.
- אחריות תמיכה חינמית ל-30 יום.
לוח זמנים משוער
| שלב | משך |
|---|---|
| ניתוח ועיצוב | 1–2 ימים |
| אימות ו-RLS | 1–2 ימים |
| אינטגרציית Frontend | 2–3 ימים |
| פריסה ובדיקות | 1–2 ימים |
| Edge Functions וזמן אמת (אופציונלי) | 2–3 ימים |
מחזור מלא אורך 5–10 ימי עבודה תלוי במורכבות.
אנחנו עובדים עם Supabase מאז הגרסאות המוקדמות שלו וצברנו ניסיון באופטימיזציית שאילתות, הגדרת שכפול ופריסה באירוח עצמי. צור קשר להגדרת Supabase מקצועית—נעריך את הפרויקט שלך ונציע את הפתרון האופטימלי.
למה RLS קריטי לאבטחה
אבטחת רמת שורה (RLS) היא לא רק תיבת סימון. ללא RLS, כל משתמש מאומת יכול לקרוא או לשנות נתונים של משתמשים אחרים אם אין הגבלות ברמת ה-API. לדוגמה, בפרויקט עדכוני החדשות, יישמנו מדיניות שמאפשרת SELECT רק לפוסטים ציבוריים או לערכים של המשתמש עצמו. זה מנע דליפות של נתונים פרטיים.
איך אופטימיזנו שאילתות בפרויקט אמיתי?
בפרויקט עם 50,000 משתמשים ו-500,000 פוסטים, שאילתות ראשוניות ללא אינדקסים ארכו 800 אלפיות שנייה. לאחר הוספת אינדקסים על `user_id` ו-`is_public`, זמן השאילתה ירד ל-12 אלפיות שנייה. Edge Functions להודעות טיפלו ב-200 בקשות בשנייה בעומס שיא. חיסכון בעלויות תשתית בהשוואה ל-Firebase עמד על 60%.







