Send ephemeral messages with Broadcast, track and synchronize state with Presence, and listen to database changes with Postgres Change Data Capture (CDC).
Guides · Reference Docs · Multiplayer Demo
This client enables you to use the following Supabase Realtime's features:
- Broadcast: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users.
- Presence: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage.
- Postgres Change Data Capture (CDC): listen for changes in your PostgreSQL database and send them to clients.
npm install @supabase/realtime-js
import { RealtimeClient } from '@supabase/realtime-js'
const client = new RealtimeClient(REALTIME_URL, {
params: {
apikey: API_KEY
},
})
const channel = client.channel('test-channel', {})
channel.subscribe((status, err) => {
if (status === 'SUBSCRIBED') {
console.log('Connected!')
}
if (status === 'CHANNEL_ERROR') {
console.log(`There was an error subscribing to channel: ${err.message}`)
}
if (status === 'TIMED_OUT') {
console.log('Realtime server did not respond in time.')
}
if (status === 'CLOSED') {
console.log('Realtime channel was unexpectedly closed.')
}
})
REALTIME_URL
is'ws:https://localhost:4000/socket'
when developing locally and'wss:https://<project_ref>.supabase.co/realtime/v1'
when connecting to your Supabase project.API_KEY
is a JWT whose claims must containexp
androle
(existing database role).- Channel name can be any
string
.
Your client can send and receive messages based on the event
.
// Setup...
const channel = client.channel('broadcast-test', { broadcast: { ack: false, self: false } })
channel.on('broadcast', { event: 'some-event' }, (payload) =>
console.log(payload)
)
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
// Send message to other clients listening to 'broadcast-test' channel
await channel.send({
type: 'broadcast',
event: 'some-event',
payload: { hello: 'world' },
})
}
})
- Setting
ack
totrue
means that thechannel.send
promise will resolve once server replies with acknowledgement that it received the broadcast message request. - Setting
self
totrue
means that the client will receive the broadcast message it sent out. - Setting
private
totrue
means that the client will use RLS to determine if the user can connect or not to a given channel.
Your client can track and sync state that's stored in the channel.
// Setup...
const channel = client.channel(
'presence-test',
{
config: {
presence: {
key: ''
}
}
}
)
channel.on('presence', { event: 'sync' }, () => {
console.log('Online users: ', channel.presenceState())
})
channel.on('presence', { event: 'join' }, ({ newPresences }) => {
console.log('New users have joined: ', newPresences)
})
channel.on('presence', { event: 'leave' }, ({ leftPresences }) => {
console.log('Users have left: ', leftPresences)
})
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const status = await channel.track({ 'user_id': 1 })
console.log(status)
}
})
Receive database changes on the client.
// Setup...
const channel = client.channel('db-changes')
channel.on('postgres_changes', { event: '*', schema: 'public' }, (payload) => {
console.log('All changes in public schema: ', payload)
})
channel.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) => {
console.log('All inserts in messages table: ', payload)
})
channel.on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'users', filter: 'username=eq.Realtime' }, (payload) => {
console.log('All updates on users table when username is Realtime: ', payload)
})
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
console.log('Ready to receive database changes!')
}
})
You can see all the channels that your client has instantiatied.
// Setup...
client.getChannels()
It is highly recommended that you clean up your channels after you're done with them.
- Remove a single channel
// Setup...
const channel = client.channel('some-channel-to-remove')
channel.subscribe()
client.removeChannel(channel)
- Remove all channels
// Setup...
const channel1 = client.channel('a-channel-to-remove')
const channel2 = client.channel('another-channel-to-remove')
channel1.subscribe()
channel2.subscribe()
client.removeAllChannels()
This repo draws heavily from phoenix-js.
MIT.