Realtime Concepts
Supabase Realtime là gì?
Overview
┌─────────────────────────────────────────────────────────────┐
│ SUPABASE REALTIME │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client A ◄────────┐ │
│ │ │
│ Client B ◄────────┼───── Realtime Server ◄──── PostgreSQL │
│ │ (WebSocket) (Changes) │
│ Client C ◄────────┘ │
│ │
│ Features: │
│ 1. Database Changes - Listen to INSERT/UPDATE/DELETE │
│ 2. Broadcast - Send messages between clients │
│ 3. Presence - Track online users │
│ │
└─────────────────────────────────────────────────────────────┘
Three Realtime Features
1. Database Changes (Postgres Changes)
// Listen to database changes
supabase.channel('db-changes')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => {
console.log('New message:', payload.new);
}
)
.subscribe();
2. Broadcast
// Send/receive messages between clients
const channel = supabase.channel('room-1');
// Send
channel.send({
type: 'broadcast',
event: 'cursor-move',
payload: { x: 100, y: 200 },
});
// Receive
channel.on('broadcast', { event: 'cursor-move' }, (payload) => {
console.log('Cursor:', payload);
});
3. Presence
// Track who's online
channel.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState();
console.log('Online users:', state);
});
Channel Concept
What is a Channel?
┌─────────────────────────────────────────────────────────────┐
│ CHANNEL │
├─────────────────────────────────────────────────────────────┤
│ │
│ Channel = Topic/Room that clients subscribe to │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Channel: "room:project-123" │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │Client A │ │Client B │ │Client C │ │ │
│ │ │(subscr) │ │(subscr) │ │(subscr) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ │ All clients receive same channel events │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ Multiple Channels: │
│ - room:project-1 │
│ - room:project-2 │
│ - user:notifications │
│ │
└─────────────────────────────────────────────────────────────┘
Creating Channels
// Create or join a channel
const channel = supabase.channel('my-channel');
// Channel naming conventions
const projectChannel = supabase.channel(`project:${projectId}`);
const userChannel = supabase.channel(`user:${userId}`);
const roomChannel = supabase.channel(`room:${roomId}`);
// Subscribe to channel
channel.subscribe((status) => {
if (status === 'SUBSCRIBED') {
console.log('Connected to channel');
}
});
Postgres Changes Deep Dive
Enable Realtime on Table
-- In Supabase Dashboard or SQL
ALTER PUBLICATION supabase_realtime ADD TABLE messages;
-- Or via Dashboard:
-- Table Editor → Select table → Enable Realtime
Event Types
// Listen to all events
channel.on('postgres_changes',
{ event: '*', schema: 'public', table: 'tasks' },
handler
);
// Specific events
channel.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'tasks' },
(payload) => console.log('New:', payload.new)
);
channel.on('postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'tasks' },
(payload) => {
console.log('Old:', payload.old);
console.log('New:', payload.new);
}
);
channel.on('postgres_changes',
{ event: 'DELETE', schema: 'public', table: 'tasks' },
(payload) => console.log('Deleted:', payload.old)
);
Filter Changes
// Filter by specific column value
channel.on('postgres_changes',
{
event: '*',
schema: 'public',
table: 'messages',
filter: 'room_id=eq.room-123', // PostgREST filter syntax
},
handler
);
// Filter by user
channel.on('postgres_changes',
{
event: '*',
schema: 'public',
table: 'notifications',
filter: `user_id=eq.${userId}`,
},
handler
);
Connection Lifecycle
Subscribe States
const channel = supabase.channel('my-channel')
.on('postgres_changes', {...}, handler)
.subscribe((status, err) => {
switch (status) {
case 'SUBSCRIBED':
console.log('Connected!');
break;
case 'TIMED_OUT':
console.log('Connection timed out');
break;
case 'CLOSED':
console.log('Connection closed');
break;
case 'CHANNEL_ERROR':
console.error('Channel error:', err);
break;
}
});
Unsubscribe
// Remove specific channel
await supabase.removeChannel(channel);
// Remove all channels
await supabase.removeAllChannels();
RLS with Realtime
Realtime Respects RLS
┌─────────────────────────────────────────────────────────────┐
│ REALTIME + RLS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Database Change ──▶ Realtime Server ──▶ RLS Check │
│ │ │
│ ┌──────┴──────┐ │
│ │ │ │
│ ▼ ▼ │
│ ALLOWED BLOCKED │
│ Send to Don't send │
│ client to client │
│ │
│ Users only receive changes they have RLS permission to see │
│ │
└─────────────────────────────────────────────────────────────┘
Example
-- RLS Policy
CREATE POLICY "Users see own tasks"
ON tasks FOR SELECT
USING (auth.uid() = user_id);
-- User A creates task for User A
-- Only User A receives the realtime event
-- User B is subscribed but doesn't receive it
Use Cases
When to Use Each Feature
| Feature |
Use Case |
| Postgres Changes |
Data sync, live feeds, notifications |
| Broadcast |
Cursor tracking, typing indicators, gaming |
| Presence |
Online users, "who's viewing" |
Example Applications
Chat App:
├── Postgres Changes: New messages
├── Broadcast: Typing indicator
└── Presence: Who's in the room
Collaborative Doc:
├── Postgres Changes: Document saves
├── Broadcast: Cursor positions
└── Presence: Active collaborators
Task Board:
├── Postgres Changes: Task updates
├── Broadcast: Drag indicators
└── Presence: Team members online
Tổng kết
Key Concepts
| Concept |
Description |
| Channel |
Named topic for communication |
| Postgres Changes |
Database events (INSERT/UPDATE/DELETE) |
| Broadcast |
Client-to-client messaging |
| Presence |
Track online/offline state |
Setup Checklist
1. Enable Realtime on table (if using Postgres Changes)
2. Create channel with unique name
3. Add event listeners
4. Subscribe to channel
5. Handle connection states
6. Cleanup on component unmount
Q&A
- Có use case realtime nào trong dự án?
- Cần track online users không?
- Đã dùng WebSocket trước đây chưa?