supabase-realtime

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Supabase Realtime

Supabase Realtime

Expert implementation guide for Supabase Realtime features focusing on scalable patterns and best practices.
一份专注于可扩展模式与最佳实践的Supabase Realtime功能专业实现指南。

Core Principles

核心原则

Always use
broadcast
over
postgres_changes
- postgres_changes is single-threaded and doesn't scale. Use broadcast with database triggers for all database change notifications.
Use dedicated topics - Never broadcast to global topics. Use granular patterns like
room:123:messages
,
user:456:notifications
.
Private channels by default - Set
private: true
for all database-triggered channels. Enable private-only mode in production.
优先使用
broadcast
而非
postgres_changes
——postgres_changes是单线程的,不具备可扩展性。所有数据库变更通知都应结合数据库触发器使用broadcast。
使用专用主题——切勿向全局主题广播消息。使用细粒度的命名模式,例如
room:123:messages
user:456:notifications
默认使用私有频道——为所有数据库触发的频道设置
private: true
。在生产环境中启用仅私有模式。

Quick Reference

快速参考

Naming Conventions

命名规范

  • Topics:
    scope:entity:id
    (e.g.,
    room:123:messages
    )
  • Events:
    entity_action
    in snake_case (e.g.,
    message_created
    )
  • 主题
    scope:entity:id
    (例如:
    room:123:messages
  • 事件:使用蛇形命名法的
    entity_action
    (例如:
    message_created

Client Setup Pattern

客户端设置示例

javascript
const channel = supabase.channel('room:123:messages', {
  config: { 
    broadcast: { self: true, ack: true },
    private: true  // Required for RLS
  }
})

// Set auth before subscribing
await supabase.realtime.setAuth()

channel
  .on('broadcast', { event: 'message_created' }, handler)
  .subscribe((status, err) => {
    if (status === 'SUBSCRIBED') console.log('Connected')
  })

// Cleanup
supabase.removeChannel(channel)
javascript
const channel = supabase.channel('room:123:messages', {
  config: { 
    broadcast: { self: true, ack: true },
    private: true  // Required for RLS
  }
})

// Set auth before subscribing
await supabase.realtime.setAuth()

channel
  .on('broadcast', { event: 'message_created' }, handler)
  .subscribe((status, err) => {
    if (status === 'SUBSCRIBED') console.log('Connected')
  })

// Cleanup
supabase.removeChannel(channel)

Database Trigger Pattern

数据库触发器示例

sql
-- Use realtime.broadcast_changes for database events
CREATE OR REPLACE FUNCTION notify_table_changes()
RETURNS TRIGGER AS $$
BEGIN
  PERFORM realtime.broadcast_changes(
    TG_TABLE_NAME || ':' || COALESCE(NEW.id, OLD.id)::text,
    TG_OP,
    TG_OP,
    TG_TABLE_NAME,
    TG_TABLE_SCHEMA,
    NEW,
    OLD
  );
  RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER messages_broadcast_trigger
  AFTER INSERT OR UPDATE OR DELETE ON messages
  FOR EACH ROW EXECUTE FUNCTION notify_table_changes();
sql
-- Use realtime.broadcast_changes for database events
CREATE OR REPLACE FUNCTION notify_table_changes()
RETURNS TRIGGER AS $$
BEGIN
  PERFORM realtime.broadcast_changes(
    TG_TABLE_NAME || ':' || COALESCE(NEW.id, OLD.id)::text,
    TG_OP,
    TG_OP,
    TG_TABLE_NAME,
    TG_TABLE_SCHEMA,
    NEW,
    OLD
  );
  RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER messages_broadcast_trigger
  AFTER INSERT OR UPDATE OR DELETE ON messages
  FOR EACH ROW EXECUTE FUNCTION notify_table_changes();

RLS Authorization

RLS授权

sql
-- Required for private channels
CREATE POLICY "room_members_can_read" ON realtime.messages
FOR SELECT TO authenticated
USING (
  topic LIKE 'room:%' AND
  EXISTS (
    SELECT 1 FROM room_members
    WHERE user_id = auth.uid()
    AND room_id = SPLIT_PART(topic, ':', 2)::uuid
  )
);

-- Required index for performance
CREATE INDEX idx_room_members_user_room ON room_members(user_id, room_id);
sql
-- Required for private channels
CREATE POLICY "room_members_can_read" ON realtime.messages
FOR SELECT TO authenticated
USING (
  topic LIKE 'room:%' AND
  EXISTS (
    SELECT 1 FROM room_members
    WHERE user_id = auth.uid()
    AND room_id = SPLIT_PART(topic, ':', 2)::uuid
  )
);

-- Required index for performance
CREATE INDEX idx_room_members_user_room ON room_members(user_id, room_id);

Implementation Checklist

实现检查清单

  • ✅ Use
    broadcast
    not
    postgres_changes
  • ✅ Dedicated topics per room/user/entity
  • ✅ Set
    private: true
    for database triggers
  • ✅ Create indexes for all RLS policy columns
  • ✅ Include cleanup/unsubscribe logic
  • ✅ Check channel state before subscribing
  • ✅ Use consistent naming conventions
  • ✅ 使用
    broadcast
    而非
    postgres_changes
  • ✅ 为每个房间/用户/实体使用专用主题
  • ✅ 为数据库触发器设置
    private: true
  • ✅ 为所有RLS策略列创建索引
  • ✅ 包含清理/取消订阅逻辑
  • ✅ 订阅前检查频道状态
  • ✅ 使用统一的命名规范

Scripts

脚本

  • scripts/create_broadcast_trigger.sql - Generic broadcast trigger function
  • scripts/migrate_from_postgres_changes.sql - Migration helper script
  • scripts/create_broadcast_trigger.sql ——通用广播触发器函数
  • scripts/migrate_from_postgres_changes.sql ——迁移辅助脚本

Advanced Topics

进阶主题

  • Framework Integration: See references/framework_patterns.md
  • Performance Optimization: See references/performance_scaling.md
  • Migration Guide: See references/migration_guide.md
  • 框架集成:请参阅references/framework_patterns.md
  • 性能优化:请参阅references/performance_scaling.md
  • 迁移指南:请参阅references/migration_guide.md