Skip to main content
The Realtime API allows you to subscribe to real-time updates for record changes using Server-Sent Events (SSE).

Connect to realtime

Establish a realtime SSE connection.
GET /api/realtime
Authentication: Optional (unauthenticated connections are guests) This endpoint establishes a persistent SSE connection that remains open to receive real-time messages.

Response

The connection returns Server-Sent Events in the following format:
id: clientId
event: PB_CONNECT
data: {"clientId":"client_id_here"}

event: subscription_name
data: {"action":"create","record":{...}}
clientId
string
Unique client connection identifier returned in the initial PB_CONNECT event
const eventSource = new EventSource('http://127.0.0.1:8090/api/realtime');

eventSource.addEventListener('PB_CONNECT', (e) => {
  console.log('Connected:', e.data);
});

eventSource.addEventListener('posts', (e) => {
  const data = JSON.parse(e.data);
  console.log('Record change:', data.action, data.record);
});

Set subscriptions

Subscribe to specific topics to receive real-time updates.
POST /api/realtime
Authentication: Optional (but required to upgrade guest connection to authenticated)

Request body

clientId
string
required
The client ID from the initial connection (returned in PB_CONNECT)
subscriptions
array
required
Array of subscription topics (max 1000 subscriptions, each max 2500 chars)

Subscription topics

You can subscribe to: Individual record:
{collection}/{recordId}
All records in collection:
{collection}/*
Collection name or ID:
posts/*
collection_id/*
With filters and options: Append query parameters to subscription topics:
posts/*?filter=status='published'&expand=author&fields=id,title
curl -X POST http://127.0.0.1:8090/api/realtime \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "client_id_from_connect",
    "subscriptions": ["posts/*", "users/*"]
  }'

Response

Returns 204 No Content on success.

Realtime events

When subscribed, you’ll receive events when records change:

Event structure

action
string
The type of change: create, update, or delete
record
object
The affected record data (respects API rules and field permissions)

Example event

{
  "action": "create",
  "record": {
    "id": "RECORD_ID",
    "collectionId": "collection_id",
    "collectionName": "posts",
    "created": "2023-01-01 00:00:00.000Z",
    "updated": "2023-01-01 00:00:00.000Z",
    "title": "New post",
    "content": "Post content"
  }
}

Client-side subscriptions

JavaScript example

let clientId = '';
const eventSource = new EventSource('http://127.0.0.1:8090/api/realtime');

// Handle connection
eventSource.addEventListener('PB_CONNECT', async (e) => {
  const data = JSON.parse(e.data);
  clientId = data.clientId;
  
  // Subscribe to topics
  await fetch('http://127.0.0.1:8090/api/realtime', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      clientId: clientId,
      subscriptions: ['posts/*', 'comments/*']
    })
  });
});

// Handle record changes
eventSource.addEventListener('posts/*', (e) => {
  const data = JSON.parse(e.data);
  
  switch (data.action) {
    case 'create':
      console.log('New post:', data.record);
      break;
    case 'update':
      console.log('Updated post:', data.record);
      break;
    case 'delete':
      console.log('Deleted post:', data.record);
      break;
  }
});

// Handle errors
eventSource.onerror = (error) => {
  console.error('SSE error:', error);
  eventSource.close();
};

Access control

Realtime subscriptions respect collection API rules:
  • listRule - For wildcard subscriptions (posts/*)
  • viewRule - For specific record subscriptions (posts/RECORD_ID)
Users only receive updates for records they have permission to view based on the collection’s API rules.

Authentication state

You can upgrade a guest connection to authenticated:
  1. Establish SSE connection (guest)
  2. Make POST request to /api/realtime with auth token
  3. Subscription permissions are updated based on the auth record
You cannot change authentication from one user to another. Downgrading from authenticated to guest is also not allowed.

Subscription options

Add query parameters to subscription topics:

Filter

Only receive events for records matching the filter:
posts/*?filter=status='published' && author=@request.auth.id

Expand

Automatically expand relation fields:
posts/*?expand=author,categories

Fields

Limit fields included in the event:
posts/*?fields=id,title,created

Combined options

posts/*?filter=featured=true&expand=author&fields=id,title,author

Connection management

Idle timeout

Connections automatically close after 5 minutes of inactivity (no events sent). The client should reconnect and resubscribe.

Reconnection

When the connection drops:
  1. Establish a new realtime connection
  2. Get new clientId from PB_CONNECT event
  3. Resubmit all subscriptions
eventSource.onerror = () => {
  eventSource.close();
  // Wait a bit before reconnecting
  setTimeout(() => {
    connectToRealtime();
  }, 1000);
};

Unsubscribing

To change subscriptions, send a new subscription list. Previous subscriptions are automatically cleared:
curl -X POST http://127.0.0.1:8090/api/realtime \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "client_id",
    "subscriptions": []  # Empty array unsubscribes from all
  }'

Performance considerations

  • Maximum 1000 subscriptions per client
  • Each subscription topic max 2500 characters
  • Realtime events are processed in chunks of 150 clients

Best practices

  1. Subscribe to specific collections rather than all records
  2. Use filters to reduce event volume
  3. Limit fields to only what you need
  4. Close connections when not needed
  5. Implement reconnection logic

Common use cases

Chat application

subscriptions: [
  'messages/*?filter=room="' + roomId + '"&sort=-created'
]

User notifications

subscriptions: [
  'notifications/*?filter=user="' + userId + '" && read=false'
]

Live dashboard

subscriptions: [
  'orders/*?filter=status="pending"',
  'users/*?filter=created>' + today
]

Collaborative editing

subscriptions: [
  'documents/' + documentId,
  'comments/*?filter=document="' + documentId + '"'
]