שילוב Supabase Realtime: הרשמה לשינויים ב-PostgreSQL
כשבונים צ'אט בזמן אמת או לוח משימות, שאילתות חוזרות (polling) אינן יעילות — הן מעמיסות על מסד הנתונים ופוגעות בחוויית המשתמש. Supabase Realtime פותר זאת באמצעות WebSocket המבוסס על שכפול לוגי של PostgreSQL, תוך האזנה ללוגי WAL. זמן השהיית השינויים הוא בדרך כלל מתחת ל-10 אלפיות שנייה, ועלויות התשתית יורדות בעד 50% בהשוואה להפעלת שרת משלכם. אם אתם זקוקים לפתרון בזמן אמת מהיר, הזמינו אותנו לשלב עבורכם את Supabase Realtime.
איך עובד שכפול לוגי של PostgreSQL
שכפול לוגי מפרסם שינויים ברמת השורה. בניגוד לשכפול סטרימינג, הוא מאפשר להירשם רק לטבלאות ספציפיות ולסנן נתונים בצד מסד הנתונים. Supabase Realtime משתמש במנגנון זה, ולא בשאילתות חוזרות על טבלאות. זה מבטיח שכל שינוי מתקבל בדיוק פעם אחת עם זמן השהיה מינימלי (בדרך כלל <10 אלפיות שנייה). פרטים נוספים זמינים בתיעוד PostgreSQL.
איך להימנע מתנאי מרוץ בעדכוני מצב
כשכמה לקוחות כותבים לאותה טבלה בו-זמנית, מסד הנתונים מבטיח אטומיות של טרנזקציות, אך קוד הלקוח עשוי לקבל אירועים בסדר לא נכון. השתמשו בעדכונים אופטימיים והשוו גרסאות שורות. ב-hook של React למטה, אנו מטפלים ב-INSERT, UPDATE ו-DELETE על ידי הסתמכות על מזהה השורה הייחודי. אם אירועים מגיעים בסדר שגוי, ניתן להחיל RSC (React Server Components) לסנכרון מלא.
התקנה והרשמה לטבלה
npm install @supabase/supabase-js import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
// Подписка на все изменения в таблице messages
const channel = supabase
.channel('public:messages')
.on(
'postgres_changes',
{
event: '*', // INSERT, UPDATE, DELETE или *
schema: 'public',
table: 'messages',
filter: `room_id=eq.${roomId}` // фильтр по значению
},
(payload) => {
if (payload.eventType === 'INSERT') {
setMessages(prev => [...prev, payload.new]);
}
if (payload.eventType === 'UPDATE') {
setMessages(prev => prev.map(m => m.id === payload.new.id ? payload.new : m));
}
if (payload.eventType === 'DELETE') {
setMessages(prev => prev.filter(m => m.id !== payload.old.id));
}
}
)
.subscribe((status) => {
console.log('Subscription status:', status);
});
// Отписка
return () => {
supabase.removeChannel(channel);
};
המסנן npm install @supabase/supabase-js הופך לתנאי import { createClient } from '@supabase/supabase-js'; const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ); // Подписка на все изменения в таблице messages const channel = supabase .channel('public:messages') .on( 'postgres_changes', { event: '*', // INSERT, UPDATE, DELETE или * schema: 'public', table: 'messages', filter: `room_id=eq.${roomId}` // фильтр по значению }, (payload) => { if (payload.eventType === 'INSERT') { setMessages(prev => [...prev, payload.new]); } if (payload.eventType === 'UPDATE') { setMessages(prev => prev.map(m => m.id === payload.new.id ? payload.new : m) ); } if (payload.eventType === 'DELETE') { setMessages(prev => prev.filter(m => m.id !== payload.old.id)); } } ) .subscribe((status) => { console.log('Subscription status:', status); }); // Отписка return () => { supabase.removeChannel(channel); }; בצד PostgreSQL. זה יעיל יותר מסינון בצד הלקוח ומפחית את העברת הנתונים בעד 80%.
React Hook להרשמה
function useRealtimeTable<T>(
table: string,
filter?: { column: string; value: string }
) {
const [data, setData] = useState<T[]>([]);
const supabase = useSupabaseClient();
useEffect(() => {
// Первоначальная загрузка
let query = supabase.from(table).select('*');
if (filter) query = query.eq(filter.column, filter.value);
query.then(({ data }) => setData(data ?? []));
// Подписка на изменения
const channel = supabase
.channel(`${table}:${filter?.value ?? 'all'}`)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table,
filter: filter ? `${filter.column}=eq.${filter.value}` : undefined,
},
(payload) => {
setData((prev) => {
if (payload.eventType === 'INSERT') return [...prev, payload.new as T];
if (payload.eventType === 'UPDATE')
return prev.map((item) =>
(item as any).id === (payload.new as any).id ? payload.new as T : item
);
if (payload.eventType === 'DELETE')
return prev.filter((item) => (item as any).id !== (payload.old as any).id);
return prev;
});
}
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [table, filter?.column, filter?.value]);
return data;
}
// Использование
const messages = useRealtimeTable<Message>('messages', {
column: 'room_id',
value: roomId,
});
ניתן להרחיב את ה-hook עם דפדוף, תמיכה במיון או עדכונים אופטימיים. הוא מקצר את הקוד מ-50 ל-10 שורות.
Broadcast — אירועים מותאמים אישית
Broadcast אינו דורש שינויים במסד הנתונים:
// Отправить событие всем подписчикам канала
await supabase.channel('cursor-positions').send({
type: 'broadcast',
event: 'cursor-moved',
payload: { x: mouseX, y: mouseY, userId: user.id }
});
// Получить
supabase.channel('cursor-positions')
.on('broadcast', { event: 'cursor-moved' }, ({ payload }) => {
updateCursorPosition(payload.userId, payload.x, payload.y);
})
.subscribe();
Broadcast נוח לסמני עכבר, מחווני הקלדה, התראות — כל דבר שאינו קשור ישירות לנתוני טבלה.
אבטחת רמת שורה (RLS)
Supabase Realtime מכבד את RLS של PostgreSQL — לקוח רואה רק שורות שיש לו הרשאת SELECT עליהן.
-- Пример RLS: пользователь видит только свои сообщения
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own messages" ON messages FOR SELECT USING (auth.uid() = user_id);זה קריטי ליישומים מרובי משתמשים: גם אם מישהו מיירט את ה-WebSocket, הוא לא יקבל נתונים של אחרים.
למה Supabase Realtime עדיף על שאילתות חוזרות ו-WebSocket ידני?
| שיטה | זמן השהיה | עומס על מסד הנתונים | מורכבות יישום | תמיכה ב-RLS |
|---|---|---|---|---|
| שאילתות חוזרות (setInterval) | שנייה עד אינסוף | גבוה (SELECT קבוע) | נמוך | כן |
| WebSocket ידני | <10 אלפיות שנייה | בינוני (triggers או NOTIFY) | גבוה | דורש לוגיקה מותאמת |
| Supabase Realtime | <10 אלפיות שנייה | נמוך (שכפול לוגי) | נמוך מאוד (3 שורות קוד) | מובנה |
Supabase Realtime מנצח בכל הפרמטרים: הוא מהיר, אינו מעמיס על מסד הנתונים, ומשלב RLS ישירות מהקופסה.
| פרמטר | SUPABASE REALTIME | WebSocket ידני |
|---|---|---|
| זמן השהיה ממוצע | <10 אלפיות שנייה | <10 אלפיות שנייה |
| מאמץ יישום | 1–2 ימים | 5–10 ימים |
| תמיכה ב-RLS | מובנה | דורש יישום |
| קנה מידה ל-10,000 חיבורים | מוכן מהקופסה | דורש כוונון |
איך אנו מיישמים Supabase Realtime
כשאתם מזמינים שילוב Supabase Realtime במפתח מלא, אנו מספקים:
- הגדרת שכפול לוגי וערוצי Realtime
- React hook (או Vue/Preact) עם טעינה ראשונית ועדכוני מצב אוטומטיים
- הגדרת מדיניות RLS לכל טבלה
- ערוצי Broadcast לאירועים מותאמים אישית (סמני עכבר, התראות)
- תיעוד: סקירת ארכיטקטורה, סכמת הרשמה, שאילתות לדוגמה
- הדרכת צוות: ניפוי שגיאות בהרשמות, ניטור מצב חיבור
- שבועיים של תמיכה לאחר מסירה
התהליך שלנו:
- ניתוח: זיהוי אילו טבלאות דורשות זמן אמת, אילו אירועים ומסננים נדרשים, איתור תנאי מרוץ.
- עיצוב: יצירת סכמת ערוצים, הכנת מיגרציות SQL ל-RLS ולשכפול.
- יישום: כתיבת ה-hook, הגדרת צד השרת, שילוב בקוד קיים.
- בדיקות: סימולציה של שינויים מקבילים על ידי 10+ משתמשים, מדידת זמן השהיה, הדמיית ניתוקי חיבור.
- פריסה: הכללה ב-CI/CD, הגדרת ניטור שגיאות (Sentry).
לוח זמנים ועלות
שילוב בסיסי (1–2 טבלאות, hook בסיסי, RLS) אורך 2 עד 4 ימי עבודה. אם יש טבלאות רבות, לוגיקה מורכבת או צורך ב-Broadcast, לוח הזמנים מתארך לשבוע. העלות מחושבת באופן אישי. צרו קשר להערכת הפרויקט שלכם.
טעויות נפוצות
-
שכחת הגדרת REPLICA IDENTITY FULL — UPDATE ו-DELETE מגיעים עם נתונים ישנים ריקים. תיקון:
room_id=eq.${roomId} - אי הגדרת RLS — לקוחות רואים את כל השורות בטבלה. ודאו מדיניות SELECT לכל טבלה מוגנת.
-
הרשמה רחבה מדי — ללא מסנן, הלקוח מקבל את כל השינויים בטבלה, מה שגורם לתעבורה מיותרת. השתמשו תמיד ב-
WHERE room_id = 'значение'.
הניסיון שלנו: 5+ שנים בפיתוח יישומים בזמן אמת, 20+ פריסות Supabase Realtime בסביבת ייצור. אנו מבטיחים פעולה יציבה בעומסים של עד 10,000 חיבורים מקבילים לכל מופע. הזמינו שילוב Supabase Realtime עוד היום וקבלו אב טיפוס עובד תוך יומיים.







