Real Time
RestAPI.com provides real-time capabilities for detecting database changes and enabling direct client-to-client communication.
Prerequisites
To use real-time subscriptions on a collection, you must configure LISTEN access:
{ "method": "LISTEN", "roleNames": ["_AUTHENTICATED_USER"] }
Without LISTEN access, clients cannot subscribe to changes on that collection. Configure this in the Developer Portal under your collection's Access settings, or in your schema JSON.
Features
- Database change detection — Subscribe to collection changes as they happen
- Client-to-client messaging — Direct peer-to-peer communication via channels
- Server-to-client messaging — Push updates from functions to connected clients
Connection Options
Choose your preferred protocol:
| Protocol | Library |
|---|---|
| Azure Web PubSub | @azure/web-pubsub-client |
| SignalR | @microsoft/signalr |
Setting Up Connections
Azure Web PubSub
import { WebPubSubClient } from '@azure/web-pubsub-client';
const client = new WebPubSubClient({
getClientAccessUrl: async () => {
const response = await fetch('/api/_rt/ws');
return response.text();
}
});
await client.start();
SignalR
import * as signalR from '@microsoft/signalr';
const connection = new signalR.HubConnectionBuilder()
.withUrl('/api/_rt/signalr')
.withAutomaticReconnect()
.build();
await connection.start();
Listening for Changes
SignalR
connection.on('change', (data) => {
console.log('Data changed:', data);
});
Web PubSub
client.on('group-message', (event) => {
console.log('Message received:', event.message);
});
Entity Subscriptions
Subscribe to database changes on a collection using the /_listen endpoint:
await fetch('/api/products/_listen', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
connectionId: myConnectionId,
type: 'ws' // or 'signalr'
})
});
Entity subscriptions are read-only — clients receive notifications but cannot send messages to entity channels. This ensures data integrity since entity notifications are controlled by the server.
Filtering Subscriptions
Subscribe to a specific item:
body: JSON.stringify({
connectionId: myConnectionId,
type: 'ws',
id: 'item-uuid' // Only receive changes for this item
})
Filter by lookup relationships:
body: JSON.stringify({
connectionId: myConnectionId,
type: 'ws',
lookupIds: ['parent-uuid-1', 'parent-uuid-2'] // Filter by parent references
})
Channel-Based Communication
Channels enable direct client-to-client messaging. There are two types:
Regular Channels
Standard channels where clients can join and send messages freely:
// Join a channel
await fetch('/api/_rt/join', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
connectionId: myConnectionId,
channel: 'my-room'
})
});
Clients can send messages via the API or directly via WebPubSub SDK:
// Via API
await fetch('/api/_rt/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
channel: 'my-room',
body: { type: 'chat', text: 'Hello!' }
})
});
// Direct client-to-client (Web PubSub) - channel prefixed with API name
client.sendToGroup('my-api:my-room', { text: 'Hello!' });
Gated Channels (Server-Controlled)
Channels with : in the name can only be joined and messaged via server-side functions. The function controls access and whether clients can send.
Use cases:
- Chat rooms - Function validates access, then clients can chat
- Broadcasts - Function adds listeners, only server sends messages
- Game lobbies - Function manages who can join and participate
// In a trigger function - add client with send permission
await post('_rt/join', {
connectionId: request.connectionId,
channel: 'game:room-123',
readOnly: false // Client can send messages
});
// Or add client as read-only listener
await post('_rt/join', {
connectionId: request.connectionId,
channel: 'announcements:system',
readOnly: true // Client can only receive
});
// Server can always send to any channel
await post('_rt/send', {
channel: 'announcements:system',
body: { text: 'Server maintenance in 10 minutes' }
});
Clients cannot directly join or send to gated channels:
// This will fail with 400 Bad Request
await fetch('/api/_rt/join', {
body: JSON.stringify({
connectionId: myConnectionId,
channel: 'game:room-123' // Rejected - contains ':'
})
});
Channel Type Summary
| Channel Type | Client Join | Client Send | Server Join | Server Send |
|---|---|---|---|---|
Regular (my-room) | Yes | Yes | Yes | Yes |
Gated (game:room) | No | configurable | Yes | Yes |
Entity (/products/_listen) | N/A | No | N/A | Yes (automatic) |
Channel Naming Rules
- Channel names with
:are server-controlled (gated channels) - Channel names cannot match collection names (use
/_listenfor entity subscriptions) - Channel names are scoped to your API (internally prefixed with
api-name:)
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/_rt/ws | GET | Get WebSocket URL for Web PubSub |
/api/_rt/signalr | GET | Get SignalR connection info |
/api/_rt/join | POST | Join a channel |
/api/_rt/send | POST | Send message to a channel |
/api/{collection}/_listen | POST | Subscribe to entity changes |
Use Cases
- Live dashboards — Update metrics in real time
- Chat applications — Direct messaging between users
- Collaborative editing — Sync changes across clients
- Notifications — Push updates to connected users
- Gaming — Real-time game state synchronization