Web (React)
Real-time chat with React + Pulsabase SDK. View on GitHub →
Build a full real-time chat application — authentication, rooms, messages, file uploads, and role-based permissions — using Pulsabase.
Web (React)
Real-time chat with React + Pulsabase SDK. View on GitHub →
Mobile (Flutter)
Native mobile chat with Flutter + Pulsabase Dart SDK. View on GitHub →
Authentication, database models with foreign keys, Row-Level Security, real-time table subscriptions, custom pub/sub channels, and file storage — all wired together with less than 20 lines of SDK calls.
| Area | Calls | What it does |
|---|---|---|
| Auth | 3 | Sign up, sign in, get current user |
| Database | 6 | Create room, list rooms, send and paginate messages |
| Real-Time | 2 | Subscribe to table publications (INSERT / DELETE) |
| Channels | 3 | Subscribe, send, and broadcast to a named room channel |
| Storage | 3 | Upload, list, and generate presigned URL for attachments |
| Total | ~17 | A complete chat — production-ready |
Messages are inserted via the SDK, persisted in PostgreSQL, and immediately pushed to all subscribers over WebSocket — no polling, no extra setup.
git clone https://github.com/pulsabase/demo-chat-webcd demo-chat-webnpm install
pulsabase loginpulsabase init # links to your Dashboard projectpulsabase db:sync # creates tables, constraints, RLS policies
# Edit src/lib/config.ts with your anon_keynpm run devgit clone https://github.com/pulsabase/demo-chat-mobilecd demo-chat-mobileflutter pub get
pulsabase loginpulsabase initpulsabase db:sync
# Edit lib/config.dart with your anon_keyflutter runimport { primary, text, foreignId, ModelSchema } from 'pulsabase';
export default class Message { @primary id: string;
@foreignId({ references: 'id', on: 'rooms', onDelete: 'CASCADE' }) room_id: string;
@foreignId({ references: 'id', on: 'users', onDelete: 'CASCADE' }) sender_id: string;
@text(false) content: string;
static schema: ModelSchema = { tableName: 'messages', publication: true, // every mutation is pushed to subscribers timestamps: true, policies: [ { action: ['SELECT'], role: 'authenticated' }, { action: ['INSERT'], role: 'authenticated' }, ], };}The demo uses two real-time techniques in parallel:
Subscribes to INSERT and DELETE events on the messages table. Every client in the room receives new messages automatically, regardless of sender.
pb.from(Message).on('INSERT', 'DELETE').listen((change) => { if (change.action === 'INSERT' && change.data.room_id === selectedRoom.id) { addMessageToUI(change.data); } else if (change.action === 'DELETE') { removeMessageFromUI(change.data.id); }});For sub-second delivery without waiting for the full database round-trip, messages are also broadcast directly to a named channel.
const channel = pb.channel(`room:${selectedRoom.id}`);
channel.on('message', (payload) => { addMessageToUI(payload);});await channel.subscribe();
// Insert to DB, then broadcast instantlyconst saved = await pb.from(Message).insert({ content, room_id });await channel.send('message', saved.data);