Bỏ qua

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

  1. Có use case realtime nào trong dự án?
  2. Cần track online users không?
  3. Đã dùng WebSocket trước đây chưa?