Realtime
NaijaBase Realtime lets you subscribe to database changes and push live updates to every connected client — no polling required. It runs over a persistent WebSocket connection backed by PostgreSQL's LISTEN/NOTIFY system.
How it works
Browser → WebSocket → NaijaBase API → PostgreSQL LISTEN/NOTIFY
When a row changes (INSERT, UPDATE, or DELETE), PostgreSQL fires a NOTIFY event. NaijaBase picks it up and forwards the payload to every subscribed client over their open WebSocket.
Connect
JavaScript SDK
import { createClient } from '@naijabase/js'
const nb = createClient('https://api.naijabase.dev', 'nb_anon_...')
// Subscribe to all changes on a table
const channel = nb.channel('public:messages')
channel
.on('postgres_changes', { event: '*', schema: 'public', table: 'messages' }, (payload) => {
console.log('Change received:', payload)
})
.subscribe()
Filter by event type
// Only listen for new rows
channel.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, handler)
// Only listen for updates
channel.on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'messages' }, handler)
// Only listen for deletes
channel.on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'messages' }, handler)
Filter by column value
// Only receive changes for a specific room
channel.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'messages', filter: 'room_id=eq.123' },
handler
)
Unsubscribe
// Remove one channel
await nb.removeChannel(channel)
// Remove all channels
await nb.removeAllChannels()
Presence (who is online)
Track which users are online in a channel:
const presenceChannel = nb.channel('room-123')
presenceChannel
.on('presence', { event: 'sync' }, () => {
const state = presenceChannel.presenceState()
console.log('Online users:', state)
})
.on('presence', { event: 'join' }, ({ newPresences }) => {
console.log('User joined:', newPresences)
})
.on('presence', { event: 'leave' }, ({ leftPresences }) => {
console.log('User left:', leftPresences)
})
.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await presenceChannel.track({ user_id: 'abc', online_at: new Date().toISOString() })
}
})
Broadcast
Send ephemeral messages to channel subscribers without touching the database:
const broadcastChannel = nb.channel('typing-indicators')
// Listen for typing events
broadcastChannel
.on('broadcast', { event: 'typing' }, ({ payload }) => {
console.log(`${payload.username} is typing...`)
})
.subscribe()
// Emit a typing event
await broadcastChannel.send({
type: 'broadcast',
event: 'typing',
payload: { username: 'Ada' },
})
Live chat example
import { useEffect, useState } from 'react'
import { createClient } from '@naijabase/js'
const nb = createClient(process.env.NEXT_PUBLIC_NAIJABASE_URL, process.env.NEXT_PUBLIC_NAIJABASE_ANON_KEY)
export default function Chat({ roomId }) {
const [messages, setMessages] = useState([])
useEffect(() => {
// Load existing messages
nb.from('messages')
.select('*')
.eq('room_id', roomId)
.order('created_at')
.then(({ data }) => setMessages(data ?? []))
// Subscribe to new messages
const channel = nb
.channel(`room-${roomId}`)
.on(
'postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages', filter: `room_id=eq.${roomId}` },
({ new: msg }) => setMessages((prev) => [...prev, msg])
)
.subscribe()
return () => { nb.removeChannel(channel) }
}, [roomId])
async function sendMessage(text) {
await nb.from('messages').insert({ room_id: roomId, text, user_id: (await nb.auth.getUser()).data.user.id })
}
return (
<div>
{messages.map((m) => <p key={m.id}>{m.text}</p>)}
</div>
)
}
RLS and Realtime
Realtime respects Row Level Security. A client only receives change events for rows their current role can SELECT. To receive events for a table, the connected user's role must have a SELECT policy on that table.
-- Users only receive changes for their own messages
CREATE POLICY "receive own messages"
ON messages
FOR SELECT
USING (user_id = auth.uid());
Direct PostgreSQL LISTEN/NOTIFY
You can also use NOTIFY from your server-side code or triggers:
-- Trigger that notifies on insert
CREATE OR REPLACE FUNCTION notify_message_insert()
RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('messages', row_to_json(NEW)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER on_message_insert
AFTER INSERT ON messages
FOR EACH ROW EXECUTE FUNCTION notify_message_insert();
Limits
| Feature | Value |
|---|---|
| Max concurrent connections | 500 per project |
| Max channels per client | 100 |
| Payload size | 64 KB |
| Presence state per key | 8 KB |