Build Realtime Chat
Set up WebSocket channels, broadcast messages, and build a live chat backend.
Build Realtime Chat
Build a chat backend where messages appear instantly for all connected users — using WebSocket channels and a message history endpoint.
What you will build
- A chat_messages table - POST /api/chat/send — send a message and broadcast it live - GET /api/chat/history — fetch recent messages - A WebSocket channel for live delivery
Steps
Create the messages table
In the Database screen, create a chat_messages table:
| Column | Type | Notes |
|--------|------|-------|
| id | Serial | Primary key |
| channel | Text | Chat room name |
| user_id | Integer | Foreign key to users |
| username | Text | Display name |
| content | Text | Message body |
| created_at | Timestamp | Default: now() |
Create a channel
Go to the Channels screen and create a new channel called chat. This channel manages WebSocket subscriptions — clients subscribe and receive all events emitted to it.
Build the send message endpoint
Create POST /api/chat/send (protected with JWT auth):
1. Verify JWT — extract user info from the token
2. Database Insert — Table: chat_messages, Data: { channel: input.channel, user_id: vars.auth.userId, username: vars.auth.username, content: input.content }, Returning: *, Assign To: message
3. Emit Event — Event: new_message, Data: vars.message, Channel: 'chat_' + input.channel
4. Respond — Status: 201, Body: { data: vars.message }
The Emit Event step broadcasts the message to every connected client instantly.
Build the history endpoint
Create GET /api/chat/history:
1. Database Select — Table: chat_messages, Where: { channel: query.channel }, Order By: created_at DESC, Limit: 50, Assign To: messages
2. Set Variable — Name: reversed, Value: vars.messages | reverse
3. Respond — Status: 200, Body: { data: vars.reversed }
Connect from the frontend
On the client side, open a WebSocket connection and subscribe:
const ws = new WebSocket('wss://your-project.spala.dev/ws');
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'subscribe', channel: 'chat_general' }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.event === 'new_message') {
// Append message to your chat UI
}
};
Add typing indicators (optional)
Create POST /api/chat/typing that emits a typing event without saving anything:
1. Emit Event — Event: typing, Data: { username: vars.auth.username }, Channel: 'chat_' + input.channel
2. Respond — Status: 200
Good to know
Channel events are scoped by name — chat_general and chat_support operate independently. Use different names to create separate chat rooms.
Realtime /channels Learn more about WebSocket channels Add User Authentication /guides/user-auth Set up the auth system for your chat users