using-ably
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUsing Ably
使用Ably
Use this skill when building, modifying, or generating code for an application that uses Ably for realtime messaging, chat, collaboration, or AI streaming.
当你为使用Ably实现实时消息传递、聊天、协作或AI流的应用构建、修改或生成代码时,使用本技能。
What Ably Is
什么是Ably
Ably is realtime infrastructure — the same way you wouldn't build your own database or CDN, you shouldn't build your own realtime messaging system. Ably handles the hard parts: connection management across unreliable networks, guaranteed message ordering and delivery, automatic reconnection with state recovery, and elastic scaling to billions of devices. It works across platforms (25+ SDKs) and protocols (WebSocket, MQTT, SSE, HTTP), so you can focus on your application logic rather than transport reliability.
Ably是实时基础设施——就像你不会自己构建数据库或CDN一样,你也不应该自己构建实时消息系统。Ably负责处理复杂的部分:不可靠网络下的连接管理、消息顺序与交付保障、带状态恢复的自动重连,以及支持数十亿设备的弹性扩容。它跨平台(25+种SDK)和跨协议(WebSocket、MQTT、SSE、HTTP)运行,让你可以专注于应用逻辑而非传输可靠性。
Documentation
文档
Always check the official docs at ably.com/docs for current API references. For LLM-optimized documentation, use ably.com/llms.txt. The guidance below covers architectural decisions and common mistakes that documentation alone doesn't prevent.
请始终查看官方文档 ably.com/docs 获取最新API参考。针对大语言模型优化的文档,请使用 ably.com/llms.txt。以下指南涵盖了仅靠文档无法避免的架构决策和常见错误。
Before Writing Code
编写代码前
Always verify against current documentation before generating Ably code. Training data goes stale — methods get renamed, parameters change, new products launch. These three checks prevent the most common LLM code generation failures.
生成Ably代码前,请始终对照最新文档进行验证。 训练数据会过时——方法会重命名、参数会变更、新产品会发布。以下三项检查可避免大语言模型代码生成最常见的失败。
1. Fetch Current Docs First
1. 先获取最新文档
Before generating code for any Ably feature:
- Fetch to get the documentation URL index
https://ably.com/llms.txt?source=using-ably - Fetch the relevant product and platform documentation pages
- Verify method names, parameters, return types, and package versions against the fetched docs
- If a feature or method doesn't appear in the docs, it probably doesn't exist — don't fabricate it
Never guess or construct doc URLs. Only use URLs from llms.txt or web search results. If a URL 404s, go back to llms.txt. Web search is also allowed as a complementary approach. If llms.txt is unreachable, fall back to web search or browse directly.
https://ably.com/docs?source=using-ably生成任何Ably功能的代码前:
- 获取 以获取文档URL索引
https://ably.com/llms.txt?source=using-ably - 获取相关产品和平台的文档页面
- 对照获取的文档验证方法名、参数、返回类型和包版本
- 如果某个功能或方法未出现在文档中,它很可能不存在——不要凭空捏造
永远不要猜测或构造文档URL。仅使用llms.txt或网页搜索结果中的URL。如果URL返回404,请回到llms.txt。网页搜索也可作为补充方法。如果llms.txt无法访问, fallback到网页搜索或直接浏览 。
https://ably.com/docs?source=using-ably2. Confirm SDK Availability
2. 确认SDK可用性
Not all products are available on all platforms. Before generating code, confirm from the fetched docs that the product has an SDK for the user's platform.
If no SDK exists:
- State clearly: "[Product] does not currently have an SDK for [platform]"
- Don't fabricate an API
- Offer alternatives: REST API directly, a different product that IS available, or MQTT/SSE protocol adapters for constrained devices
并非所有产品都支持所有平台。生成代码前,请从获取的文档中确认该产品是否有适用于用户平台的SDK。
如果没有对应SDK:
- 明确说明:"[产品] 目前没有适用于[平台]的SDK"
- 不要捏造API
- 提供替代方案:直接使用REST API、选择可用的其他产品,或为受限设备使用MQTT/SSE协议适配器
3. Post-Generation Check
3. 代码生成后检查
After generating code, verify these common-mistake patterns:
- API key in client code? Must use /
authUrlwith JWT or token auth (see Section 3)authCallback - Chat SDK: before subscribing? Subscribe first to avoid messages being silently lost (see Section 7)
attach() - React: Ably client created inside a component without cleanup? Create once outside and pass via provider, or use useEffect with proper cleanup (see Section 8)
- Server-side: Realtime SDK used when REST would suffice? Default to for publishing, token generation, history (see Section 2)
Ably.Rest - Connection cleanup on unmount/exit? Call (see Section 5)
realtime.close() - Reimplementing built-in behavior? Don't add custom debounce for typing indicators, custom throttling for cursors, or custom conflict resolution for LiveObjects (see Section 9)
生成代码后,验证以下常见错误模式:
- 客户端代码中是否包含API密钥? 必须使用/
authUrl结合JWT或令牌认证(见第3节)authCallback - Chat SDK:订阅前是否调用了? 应先订阅以避免消息被静默丢失(见第7节)
attach() - React:是否在组件内部创建Ably客户端且未清理? 应在外部创建一次并通过provider传递,或使用useEffect并正确清理(见第8节)
- 服务端:是否在REST足够的情况下使用了Realtime SDK? 发布、令牌生成、历史查询默认使用(见第2节)
Ably.Rest - 卸载/退出时是否清理了连接? 调用(见第5节)
realtime.close() - 是否重新实现了内置功能? 不要为打字指示器添加自定义防抖、为光标添加自定义节流,或为LiveObjects添加自定义冲突解决(见第9节)
1. Understand the Product and SDK Landscape
1. 了解产品与SDK生态
Ably is a multi-product platform, not a single SDK. Choosing the wrong product or SDK is the most common integration mistake.
Ably是多产品平台,而非单一SDK。选择错误的产品或SDK是最常见的集成错误。
Product Layer: What Are You Building?
产品层:你要构建什么?
| Product | Use Case | SDK |
|---|---|---|
| Pub/Sub | Core messaging: live dashboards, notifications, IoT, event streaming | |
| AI Transport | Streaming LLM tokens, multi-agent coordination, resumable AI sessions | |
| LiveObjects | Shared mutable state: counters, key-value maps, collaborative data | |
| LiveSync | Database-to-frontend sync (PostgreSQL change streams) | |
| Chat | Chat rooms, typing indicators, reactions, message history, moderation | |
| Spaces | Collaborative cursors, avatar stacks, member locations, component locking | |
| 产品 | 适用场景 | SDK |
|---|---|---|
| Pub/Sub | 核心消息传递:实时仪表板、通知、物联网、事件流 | |
| AI Transport | LLM令牌流、多Agent协调、可恢复AI会话 | |
| LiveObjects | 共享可变状态:计数器、键值映射、协作数据 | |
| LiveSync | 数据库到前端的同步(PostgreSQL变更流) | |
| Chat | 聊天室、打字指示器、消息反应、消息历史、内容审核 | |
| Spaces | 协作光标、头像堆叠、成员位置、组件锁定 | |
SDK Architecture: Two Layers
SDK架构:两层结构
Layer 1 — Core SDKs (used directly for Pub/Sub, AI Transport, LiveObjects, LiveSync):
| SDK | Use When | Connection |
|---|---|---|
| Client needs to subscribe to messages, presence, or state changes | Persistent WebSocket |
| Server-side publish only, token generation, history queries | Stateless HTTP |
These are the foundation. AI Transport, LiveObjects, and LiveSync all use the core Realtime SDK directly — they are patterns and plugins on top of Pub/Sub, not separate SDKs.
Layer 2 — Product SDKs (higher-level abstractions for specific use cases):
| SDK | Purpose |
|---|---|
| Rooms, typing indicators, reactions, message history, moderation |
| Cursors, avatar stacks, member locations, component locking |
These product SDKs depend on the core Realtime SDK. You pass an instance when creating them. The underlying Realtime knowledge (auth, channels, presence) still applies.
Ably.Realtime第一层 — 核心SDK(直接用于Pub/Sub、AI Transport、LiveObjects、LiveSync):
| SDK | 使用场景 | 连接方式 |
|---|---|---|
| 客户端需要订阅消息、在线状态或状态变更 | 持久化WebSocket |
| 服务端仅需发布、令牌生成、历史查询 | 无状态HTTP |
这些是基础。AI Transport、LiveObjects和LiveSync都直接使用核心Realtime SDK——它们是基于Pub/Sub的模式和插件,而非独立SDK。
第二层 — 产品SDK(针对特定场景的高层抽象):
| SDK | 用途 |
|---|---|
| 聊天室、打字指示器、消息反应、消息历史、内容审核 |
| 协作光标、头像堆叠、成员位置、组件锁定 |
这些产品SDK依赖核心Realtime SDK。创建它们时需传入实例。底层的Realtime知识(认证、通道、在线状态)仍然适用。
Ably.RealtimeDecision Rules
决策规则
Q: Is this server-side or client-side?
├── Server-side:
│ ├── Publishing, token generation, history → Use Ably.Rest (stateless HTTP)
│ └── Need persistent connection (AI token streaming, subscribing) → Use Ably.Realtime
└── Client-side → Use Ably.Realtime, then:
├── Building chat? → Use @ably/chat (pass Realtime instance)
├── Building collaboration? → Use @ably/spaces (pass Realtime instance)
├── Need shared state? → Use LiveObjects plugin with Realtime
├── Syncing database? → Use @ably-labs/models with ADBC connector
└── Otherwise → Use Ably.Realtime directly (Pub/Sub, AI Transport)Server-side rule of thumb: Default to for server operations. Only use on the server when you genuinely need a persistent connection — for example, an AI agent streaming tokens to clients, or a backend service that subscribes to events.
Ably.RestAbly.RealtimeQ: 是服务端还是客户端场景?
├── 服务端:
│ ├── 发布、令牌生成、历史查询 → 使用Ably.Rest(无状态HTTP)
│ └── 需要持久连接(AI令牌流、订阅事件) → 使用Ably.Realtime
└── 客户端 → 使用Ably.Realtime,然后:
├── 构建聊天功能? → 使用@ably/chat(传入Realtime实例)
├── 构建协作功能? → 使用@ably/spaces(传入Realtime实例)
├── 需要共享状态? → 结合Realtime使用LiveObjects插件
├── 同步数据库? → 使用@ably-labs_models结合ADBC连接器
└── 其他场景 → 直接使用Ably.Realtime(Pub/Sub、AI Transport)服务端经验法则:服务端操作默认使用。仅当确实需要持久连接时才在服务端使用——例如,AI Agent向客户端流式传输令牌,或后端服务订阅事件。
Ably.RestAbly.RealtimeCommon Mistakes
常见错误
javascript
// WRONG: Using REST SDK then polling for messages
const rest = new Ably.Rest({ key: '...' });
setInterval(async () => {
const history = await rest.channels.get('updates').history();
}, 1000);
// RIGHT: Using Realtime SDK to subscribe
const realtime = new Ably.Realtime({ key: '...' });
realtime.channels.get('updates').subscribe((msg) => {
console.log('Received:', msg.data);
});
// WRONG: Using raw Pub/Sub for chat when Chat SDK exists
const channel = realtime.channels.get('chat-room');
channel.subscribe((msg) => { /* manually handling typing, reactions, history... */ });
// RIGHT: Using Chat SDK which handles all chat patterns
import { ChatClient } from '@ably/chat';
const chat = new ChatClient(realtime);
const room = await chat.rooms.get('my-room');
room.messages.subscribe((msg) => console.log(msg.text));
await room.typing.keystroke(); // built-in typing indicatorsjavascript
// 错误:使用REST SDK然后轮询消息
const rest = new Ably.Rest({ key: '...' });
setInterval(async () => {
const history = await rest.channels.get('updates').history();
}, 1000);
// 正确:使用Realtime SDK订阅
const realtime = new Ably.Realtime({ key: '...' });
realtime.channels.get('updates').subscribe((msg) => {
console.log('收到消息:', msg.data);
});
// 错误:已有Chat SDK却使用原生Pub/Sub构建聊天功能
const channel = realtime.channels.get('chat-room');
channel.subscribe((msg) => { /* 手动处理打字、反应、历史... */ });
// 正确:使用Chat SDK处理所有聊天模式
import { ChatClient } from '@ably/chat';
const chat = new ChatClient(realtime);
const room = await chat.rooms.get('my-room');
room.messages.subscribe((msg) => console.log(msg.text));
await room.typing.keystroke(); // 内置打字指示器2. Use Modern SDK Patterns (v2.x)
2. 使用现代SDK模式(v2.x)
Ably's JavaScript SDK v2.x uses async/await. Never generate callback-style 1.x patterns.
javascript
// WRONG: Old 1.x callback pattern (deprecated)
const channel = realtime.channels.get('updates');
channel.subscribe('event', function(message) {
console.log(message.data);
});
channel.publish('event', { text: 'hello' }, function(err) {
if (err) console.error(err);
});
// RIGHT: Modern 2.x async/await pattern
const channel = realtime.channels.get('updates');
channel.subscribe('event', (message) => {
console.log(message.data);
});
await channel.publish('event', { text: 'hello' });Imports and initialization — keep it simple:
javascript
// RIGHT: Standard ESM import
import Ably from 'ably';
const realtime = new Ably.Realtime({ key: 'your-key' });
const rest = new Ably.Rest({ key: 'your-key' });
// RIGHT: Chat SDK import
import { ChatClient } from '@ably/chat';
// WRONG: Don't invent type-based initialization
const options: Ably.Types.ClientOptions = { ... }; // unnecessaryIf you hit ESM/CommonJS import errors, check that your has or , and that you're importing from (not subpaths). Don't try multiple import strategies — check the SDK README for your environment.
tsconfig.json"moduleResolution": "bundler""node16"'ably'Server-side: Default to REST SDK. Only use on the server when you need a persistent connection.
Ably.Realtimejavascript
// WRONG: Realtime SDK on server just to publish
const realtime = new Ably.Realtime({ key: '...' }); // opens WebSocket unnecessarily
await realtime.channels.get('events').publish('update', data);
// RIGHT: REST SDK for server-side publish
const rest = new Ably.Rest({ key: '...' }); // stateless HTTP
await rest.channels.get('events').publish('update', data);
// RIGHT: Realtime on server for AI streaming (message-per-response pattern)
// See ably.com/docs/ai-transport/token-streaming/message-per-response?source=using-ably
const channel = realtime.channels.get('conversation:123');
const { serials: [msgSerial] } = await channel.publish({ name: 'response', data: '' });
for await (const event of llmStream) {
if (event.type === 'token') {
channel.appendMessage({ serial: msgSerial, data: event.text });
}
}Ably的JavaScript SDK v2.x使用async/await。永远不要生成回调风格的1.x模式。
javascript
// 错误:旧版1.x回调模式(已废弃)
const channel = realtime.channels.get('updates');
channel.subscribe('event', function(message) {
console.log(message.data);
});
channel.publish('event', { text: 'hello' }, function(err) {
if (err) console.error(err);
});
// 正确:现代2.x async/await模式
const channel = realtime.channels.get('updates');
channel.subscribe('event', (message) => {
console.log(message.data);
});
await channel.publish('event', { text: 'hello' });导入与初始化 — 保持简洁:
javascript
// 正确:标准ESM导入
import Ably from 'ably';
const realtime = new Ably.Realtime({ key: 'your-key' });
const rest = new Ably.Rest({ key: 'your-key' });
// 正确:Chat SDK导入
import { ChatClient } from '@ably/chat';
// 错误:不要发明基于类型的初始化
const options: Ably.Types.ClientOptions = { ... }; // 不必要如果遇到ESM/CommonJS导入错误,请检查你的是否设置了或,且你是从导入(而非子路径)。不要尝试多种导入策略——请查看SDK README获取对应环境的说明。
tsconfig.json"moduleResolution": "bundler""node16"'ably'服务端:默认使用REST SDK。 仅当需要持久连接时才在服务端使用。
Ably.Realtimejavascript
// 错误:仅为发布消息就在服务端使用Realtime SDK
const realtime = new Ably.Realtime({ key: '...' }); // 不必要地开启WebSocket
await realtime.channels.get('events').publish('update', data);
// 正确:服务端发布使用REST SDK
const rest = new Ably.Rest({ key: '...' }); // 无状态HTTP
await rest.channels.get('events').publish('update', data);
// 正确:服务端使用Realtime SDK进行AI流传输(消息逐段响应模式)
// 参考 ably.com/docs/ai-transport/token-streaming/message-per-response?source=using-ably
const channel = realtime.channels.get('conversation:123');
const { serials: [msgSerial] } = await channel.publish({ name: 'response', data: '' });
for await (const event of llmStream) {
if (event.type === 'token') {
channel.appendMessage({ serial: msgSerial, data: event.text });
}
}3. Authentication
3. 认证
API keys are for server-side only. Never expose them to clients.
API keys don't expire. If leaked, attackers have indefinite access until you regenerate the key.
API密钥仅用于服务端。永远不要向客户端暴露API密钥。
API密钥不会过期。如果泄露,攻击者将拥有无限访问权限,直到你重新生成密钥。
Recommended: JWT Authentication
推荐:JWT认证
JWT is the recommended authentication method for client-side applications.
Why JWT over Ably tokens:
- No round-trip to Ably servers — your backend creates and signs the JWT directly
- Integrates with existing auth systems — uses standard JWT libraries you already have
- Capabilities defined per-token — not limited to what's configured on the API key
- Works everywhere — including environments without an Ably SDK (MQTT, embedded devices)
JWT Claims:
- (header): Your Ably API key name (e.g.,
kid)xVLyHw.abcdef - : Issued-at timestamp (seconds)
iat - : Expiration timestamp (seconds)
exp - : JSON string of channel permissions
x-ably-capability - (optional): Client identity for presence
x-ably-clientId
javascript
// Server-side: Create JWT for client
import jwt from 'jsonwebtoken';
app.post('/api/ably-auth', (req, res) => {
const apiKey = process.env.ABLY_API_KEY; // 'appId.keyId:keySecret'
const [keyName, keySecret] = apiKey.split(':');
const token = jwt.sign(
{
'x-ably-capability': JSON.stringify({
'room:*': ['subscribe', 'publish', 'presence'],
[`notifications:${req.user.id}`]: ['subscribe'],
}),
'x-ably-clientId': req.user.id,
},
keySecret,
{
expiresIn: '1h',
keyid: keyName,
}
);
res.json(token);
});
// Client-side: Use JWT with Ably
const realtime = new Ably.Realtime({
authUrl: '/api/ably-auth',
authMethod: 'POST',
});JWT是客户端应用的推荐认证方式。
为什么选择JWT而非Ably令牌:
- 无需与Ably服务器往返——你的后端直接创建并签名JWT
- 与现有认证系统集成——使用你已有的标准JWT库
- 每个令牌可定义独立权限——不受API密钥配置的限制
- 全场景适用——包括没有Ably SDK的环境(MQTT、嵌入式设备)
JWT声明:
- (头部):你的Ably API密钥名称(例如:
kid)xVLyHw.abcdef - :签发时间戳(秒)
iat - :过期时间戳(秒)
exp - :通道权限的JSON字符串
x-ably-capability - (可选):用于在线状态的客户端标识
x-ably-clientId
javascript
// 服务端:为客户端创建JWT
import jwt from 'jsonwebtoken';
app.post('/api/ably-auth', (req, res) => {
const apiKey = process.env.ABLY_API_KEY; // 'appId.keyId:keySecret'
const [keyName, keySecret] = apiKey.split(':');
const token = jwt.sign(
{
'x-ably-capability': JSON.stringify({
'room:*': ['subscribe', 'publish', 'presence'],
[`notifications:${req.user.id}`]: ['subscribe'],
}),
'x-ably-clientId': req.user.id,
},
keySecret,
{
expiresIn: '1h',
keyid: keyName,
}
);
res.json(token);
});
// 客户端:结合JWT使用Ably
const realtime = new Ably.Realtime({
authUrl: '/api/ably-auth',
authMethod: 'POST',
});Alternative: Ably Token Requests
替代方案:Ably令牌请求
JWT is recommended for most use cases, but Ably tokens may be preferred when your capability list is too large for JWT size limits (JWTs must fit within HTTP headers, ~8KB), or when you need to keep capabilities confidential since JWTs can be decoded by clients. The trade-off is that Ably token integration is more involved and doesn't support all JWT functionality like channel-scoped claims and per-connection rate limits.
javascript
// Ably's native token system (requires round-trip to Ably servers from backend)
app.post('/api/ably-token', (req, res) => {
const client = new Ably.Rest({ key: process.env.ABLY_API_KEY });
client.auth.createTokenRequest({
clientId: req.user.id,
capability: { 'room:*': ['subscribe', 'publish'] },
}).then(tokenRequest => res.json(tokenRequest));
});JWT适用于大多数场景,但当你的权限列表过大超出JWT大小限制(JWT必须适配HTTP头,约8KB),或你需要对权限保密(客户端可解码JWT)时,可优先选择Ably令牌。缺点是Ably令牌集成更复杂,且不支持JWT的所有功能,比如通道级声明和每连接速率限制。
javascript
// Ably原生令牌系统(需要后端与Ably服务器往返)
app.post('/api/ably-token', (req, res) => {
const client = new Ably.Rest({ key: process.env.ABLY_API_KEY });
client.auth.createTokenRequest({
clientId: req.user.id,
capability: { 'room:*': ['subscribe', 'publish'] },
}).then(tokenRequest => res.json(tokenRequest));
});Rules
规则
- Server-side: API key is fine ()
{ key: 'appId.keyId:keySecret' } - Client-side: Always use or
authUrl— never embed the API key or pass a staticauthCallbackdirectly (it won't auto-refresh on expiry)token - Set if you need presence features — it's required for presence
clientId - Use capabilities to restrict what channels and operations each client can access
- 服务端:可直接使用API密钥()
{ key: 'appId.keyId:keySecret' } - 客户端:始终使用或
authUrl——永远不要嵌入API密钥或直接传递静态authCallback(过期后无法自动刷新)token - 如果需要在线状态功能,请设置——这是在线状态的必填项
clientId - 使用权限限制每个客户端可访问的通道和操作
4. Channel Design
4. 通道设计
Channels separate messages into topics. Get the naming right early — it's hard to change later.
Rules:
- Use as a hierarchy separator:
:,chat:room-123orders:user-456 - Channel names are case-sensitive
- Don't create one channel per message — channels are long-lived topics
- Use rules in the dashboard to apply settings (e.g., persistence, push notifications) to groups of channels
Common patterns:
chat:room-{roomId} # Chat rooms
notifications:user-{userId} # Per-user notifications
cursors:doc-{docId} # Collaborative editing
events:{eventType} # Event streaming
conversation:{sessionId} # AI Transport sessions通道将消息划分为不同主题。尽早确定正确的命名——后期修改难度大。
规则:
- 使用作为层级分隔符:
:,chat:room-123orders:user-456 - 通道名称区分大小写
- 不要为每条消息创建一个通道——通道是长期存在的主题
- 使用控制台中的规则为一组通道应用设置(例如:持久化、推送通知)
常见模式:
chat:room-{roomId} # 聊天室
notifications:user-{userId} # 按用户推送的通知
cursors:doc-{docId} # 协作编辑
events:{eventType} # 事件流
conversation:{sessionId} # AI Transport会话5. Connection Management
5. 连接管理
The Realtime SDK manages reconnection automatically. Don't fight it.
Rules:
- Don't manually reconnect — the SDK handles transient failures with exponential backoff
- Listen to connection state changes to update UI:
javascript
realtime.connection.on('connected', () => { /* online */ });
realtime.connection.on('disconnected', () => { /* temporarily offline */ });
realtime.connection.on('suspended', () => { /* offline for extended period */ });- Call when done (component unmount, page unload, etc.)
realtime.close() - Messages published while disconnected are received on reconnection (within the 2-minute recovery window)
- For AI Transport (client-side): use channel to hydrate returning clients with recent messages:
rewind
javascript
const channel = realtime.channels.get('conversation:123', {
params: { rewind: '2m' } // Replay last 2 minutes on attach
});Realtime SDK会自动管理重连。不要手动干预。
规则:
- 不要手动重连——SDK会用指数退避处理临时故障
- 监听连接状态变更以更新UI:
javascript
realtime.connection.on('connected', () => { /* 在线 */ });
realtime.connection.on('disconnected', () => { /* 临时离线 */ });
realtime.connection.on('suspended', () => { /* 长时间离线 */ });- 使用完毕时调用(组件卸载、页面关闭等场景)
realtime.close() - 离线时发布的消息会在重连后接收(2分钟恢复窗口内)
- 对于AI Transport(客户端):使用通道功能为重新连接的客户端恢复最近消息:
rewind
javascript
const channel = realtime.channels.get('conversation:123', {
params: { rewind: '2m' } // 连接时重播最近2分钟的消息
});6. Presence
6. 在线状态
Presence tracks which clients are on a channel. Use it for "who's online" features.
Rules:
- Set during auth — it's required for presence
clientId - Call to join,
channel.presence.enter()to update data,channel.presence.update()to departchannel.presence.leave() - Use for current members,
channel.presence.get()for changeschannel.presence.subscribe() - If a client disconnects ungracefully, Ably removes them after ~15 seconds (not instantly)
- Don't use presence for high-frequency data (cursor positions, typing coordinates) — use channels instead. Presence is for low-frequency state (online/offline, user status)
- For Chat: use instead of raw channel presence
room.presence - For Spaces: use /
space.enter()hook. Spaces has dedicateduseMembers()API for cursor positionscursors
在线状态用于跟踪哪些客户端在通道上。可用于显示“谁在线”之类的功能。
规则:
- 认证时设置——这是在线状态的必填项
clientId - 调用加入,
channel.presence.enter()更新数据,channel.presence.update()离开channel.presence.leave() - 使用获取当前成员,
channel.presence.get()监听变更channel.presence.subscribe() - 如果客户端异常断开,Ably会在约15秒后移除其在线状态(不会立即移除)
- 不要用在线状态传输高频数据(光标位置、打字坐标)——改用通道。在线状态适用于低频状态(在线/离线、用户状态)
- 对于Chat:使用而非原生通道在线状态
room.presence - 对于Spaces:使用/
space.enter()钩子。Spaces有专门的useMembers()API处理光标位置cursors
7. Chat SDK: Critical Lifecycle
7. Chat SDK:关键生命周期
If using , these are the most common mistakes:
@ably/chatAlways subscribe before calling . Attaching without subscribing first causes silent message loss — the worst kind of bug.
attach()javascript
const room = await chat.rooms.get('my-room');
// WRONG: Attach without subscribing first — messages silently lost
await room.attach();
room.messages.subscribe((msg) => console.log(msg.text)); // may miss messages during attach
// RIGHT: Subscribe first, then attach
room.messages.subscribe((msg) => console.log(msg.text));
await room.attach();Use the actual API. The Chat SDK does NOT have: threading, read receipts, file attachments, or . Use to send messages.
room.messages.broadcast()room.messages.send()Don't mix Chat and Pub/Sub patterns. If you're using , use , , — not raw / .
@ably/chatroom.messagesroom.typingroom.reactionschannel.subscribe()channel.publish()Built-in behavior you don't need to reimplement:
- Typing indicator frequency control is handled by the SDK automatically
- Room reactions delivery is handled by the SDK
- Message ordering and history pagination are built-in
如果使用,以下是最常见的错误:
@ably/chat务必先订阅再调用。 先调用attach而不订阅会导致消息静默丢失——这是最严重的一类bug。
attach()javascript
const room = await chat.rooms.get('my-room');
// 错误:先attach再订阅——消息会静默丢失
await room.attach();
room.messages.subscribe((msg) => console.log(msg.text)); // 可能会丢失attach过程中的消息
// 正确:先订阅再attach
room.messages.subscribe((msg) => console.log(msg.text));
await room.attach();使用真实API。 Chat SDK不支持:线程、已读回执、文件附件或。请使用发送消息。
room.messages.broadcast()room.messages.send()不要混合Chat与Pub/Sub模式。 如果你使用,请使用、、——不要使用原生 / 。
@ably/chatroom.messagesroom.typingroom.reactionschannel.subscribe()channel.publish()无需重新实现的内置功能:
- 打字指示器的频率控制由SDK自动处理
- 房间消息反应的交付由SDK处理
- 消息顺序和历史分页是内置功能
8. React Integration
8. React集成
Ably provides React hooks for each product:
| Product | Package | Key Hooks |
|---|---|---|
| Pub/Sub | | |
| Chat | | |
| Spaces | | |
Critical rule: Don't create a new Ably client on every render. This creates a new WebSocket connection each time — a memory leak.
javascript
// WRONG: Creates new connection every render — memory leak
function Chat() {
const ably = new Ably.Realtime({ authUrl: '/api/ably-auth' });
// ...
}
// RIGHT: Create client once, pass via provider
const ably = new Ably.Realtime({ authUrl: '/api/ably-auth' });
function App() {
return (
<AblyProvider client={ably}>
<ChannelProvider channelName="chat:room-1">
<Chat />
</ChannelProvider>
</AblyProvider>
);
}
// ALSO RIGHT: Create in useEffect with proper cleanup
function App() {
const [client, setClient] = useState(null);
useEffect(() => {
const ably = new Ably.Realtime({ authUrl: '/api/ably-auth' });
setClient(ably);
return () => ably.close();
}, []);
// ...
}Ably为每个产品提供React钩子:
| 产品 | 包 | 核心钩子 |
|---|---|---|
| Pub/Sub | | |
| Chat | | |
| Spaces | | |
关键规则:不要在每次渲染时创建新的Ably客户端。 这会每次创建新的WebSocket连接——导致内存泄漏。
javascript
// 错误:每次渲染创建新连接——内存泄漏
function Chat() {
const ably = new Ably.Realtime({ authUrl: '/api/ably-auth' });
// ...
}
// 正确:创建一次客户端,通过provider传递
const ably = new Ably.Realtime({ authUrl: '/api/ably-auth' });
function App() {
return (
<AblyProvider client={ably}>
<ChannelProvider channelName="chat:room-1">
<Chat />
</ChannelProvider>
</AblyProvider>
);
}
// 同样正确:在useEffect中创建并正确清理
function App() {
const [client, setClient] = useState(null);
useEffect(() => {
const ably = new Ably.Realtime({ authUrl: '/api/ably-auth' });
setClient(ably);
return () => ably.close();
}, []);
// ...
}9. Spaces and LiveObjects: Built-in Behavior
9. Spaces与LiveObjects:内置功能
When using Spaces ():
@ably/spaces- Cursor position batching is built-in — don't implement custom throttling
- Member location tracking is built-in via
space.locations - Component locking handles contention automatically via
space.locks
When using LiveObjects:
- Available data structures are Maps and Counters only (no custom object types)
- Conflict resolution is built-in — don't implement your own
- State recovery after reconnection is automatic
- Prefer the path-based API over the instance API for simplicity
使用Spaces ()时:
@ably/spaces- 光标位置批处理是内置功能——不要实现自定义节流
- 成员位置跟踪通过内置支持
space.locations - 组件锁定通过自动处理竞争
space.locks
使用LiveObjects时:
- 仅支持Map和Counter两种数据结构(无自定义对象类型)
- 冲突解决是内置功能——不要自行实现
- 重连后的状态恢复是自动的
- 为简洁起见,优先使用基于路径的API而非实例API
10. LiveSync: Database-to-Frontend
10. LiveSync:数据库到前端
LiveSync connects your database to frontends: Database → ADBC Connector → Ably Channels → Models SDK → Frontend.
Key constraints: PostgreSQL requires logical replication (14+, WAL level change needs restart). MongoDB requires a replica set (no standalone). The Database Connector handles channel mapping automatically once you configure an ingress rule.
Always fetch current LiveSync docs from ably.com/docs/livesync — database permissions and connector configuration change across versions.
LiveSync将数据库连接到前端:数据库 → ADBC连接器 → Ably通道 → Models SDK → 前端。
关键限制: PostgreSQL需要逻辑复制(14+版本,WAL级别变更需要重启)。MongoDB需要副本集(不支持单实例)。配置入口规则后,数据库连接器会自动处理通道映射。
请始终获取最新LiveSync文档 来自 ably.com/docs/livesync——数据库权限和连接器配置会随版本变更。
11. Production Checklist
11. 生产环境检查清单
Before going to production, verify:
- No API keys in client code — use JWT or token auth via /
authUrlauthCallback - Capabilities are scoped — don't grant to clients; restrict to specific channels and operations
{"*":["*"]} - Connection cleanup — call on unmount/unload to avoid connection leaks
realtime.close() - Error handling — listen to and handle auth failures gracefully
connection.on('failed') - Channel detach — detach from channels you no longer need ()
channel.detach() - Message size — messages are limited to 64KB by default; if you're hitting this, split payloads or reconsider message design
- Idempotent publishing — set unique message IDs when exactly-once delivery matters
- — set this if publishers don't need to receive their own messages (saves bandwidth and cost)
echoMessages: false - Rate limits — Ably enforces rate limits (e.g., 50 messages/second per channel, 200 channels per connection). If you're hitting them, check your publish frequency and channel fan-out. Read the limits documentation
上线前,请验证:
- 客户端代码中无API密钥 —— 使用JWT或通过/
authUrl进行令牌认证authCallback - 权限已限定范围 —— 不要为客户端授予权限;限制为特定通道和操作
{"*":["*"]} - 连接已清理 —— 在卸载/关闭时调用以避免连接泄漏
realtime.close() - 错误处理已实现 —— 监听并优雅处理认证失败
connection.on('failed') - 已分离不再需要的通道 —— 调用
channel.detach() - 消息大小合规 —— 默认消息大小限制为64KB;如果超出,请拆分 payload或重新设计消息
- 幂等发布已配置 —— 当需要精确一次交付时,设置唯一消息ID
- 已设置—— 如果发布者无需接收自己的消息,请设置此项(节省带宽和成本)
echoMessages: false - 已考虑速率限制 —— Ably会强制执行速率限制(例如:每个通道50条消息/秒,每个连接200个通道)。如果触发限制,请检查发布频率和通道扇出。阅读 限制文档
Error Handling
错误处理
Ably error codes can be broad (e.g., 40000, 50000) — always read the error message text, not just the code. Every error code has a help page at .
https://help.ably.io/error/{code}Ably错误代码可能范围较广(例如:40000、50000)——请始终阅读错误消息文本,而非仅看代码。每个错误代码都有对应的帮助页面:。
https://help.ably.io/error/{code}12. Quick Reference
12. 快速参考
Platform SDKs: JavaScript, React, Python, Ruby, Java, Kotlin, Swift, .NET, Go, PHP, Flutter — ably.com/docs/sdks
Ably CLI: Install with to publish test messages, subscribe to channels, and verify setup. Run to discover commands.
npm install -g @ably/cliably --helpFor API references, start at ably.com/docs.
平台SDK: JavaScript、React、Python、Ruby、Java、Kotlin、Swift、.NET、Go、PHP、Flutter — ably.com/docs/sdks
Ably CLI: 使用安装,用于发布测试消息、订阅通道并验证设置。运行查看命令。
npm install -g @ably/cliably --helpAPI参考请从 ably.com/docs 开始。