Overview
InsForge Realtime provides a powerful event-driven messaging system that bridges database changes to connected clients. Events are triggered by PostgreSQL triggers, delivered through WebSocket connections via Socket.IO, and optionally forwarded to webhook endpoints.Technology Stack
Core Components
| Component | Technology | Purpose |
|---|---|---|
| Realtime Schema | PostgreSQL | Channels, messages tables and publish function |
| Permission Model | Grants + RLS (optional) | Controls subscribe and publish access |
| Notification Bridge | pg_notify | Bridges database to Node.js process |
| Realtime Manager | Node.js | Listens for notifications, dispatches messages |
| WebSocket Server | Socket.IO | Bidirectional client communication |
| Webhook Sender | Axios | HTTP delivery to external endpoints |
| SDK | @insforge/sdk | Client-side subscription and messaging |
Database Schema
Channels Table
Therealtime.channels table defines available channel patterns and their configuration:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
pattern | TEXT | Channel name pattern (e.g., orders, order:%) |
description | TEXT | Human-readable description |
webhook_urls | TEXT[] | Array of webhook endpoints |
enabled | BOOLEAN | Whether the channel is active |
Channel patterns use
: as separator and % for wildcards (SQL LIKE pattern). For example, order:% matches order:123, order:456, etc.Messages Table
Therealtime.messages table stores all published messages for audit purposes:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
event_name | TEXT | Event type (e.g., order_created) |
channel_id | UUID | Reference to channels table |
channel_name | TEXT | Resolved channel name (e.g., order:123) |
payload | JSONB | Event data |
sender_type | TEXT | system (trigger) or user (client) |
sender_id | UUID | User ID for client-initiated messages |
ws_audience_count | INTEGER | WebSocket subscribers at time of delivery |
wh_audience_count | INTEGER | Webhook URLs configured |
wh_delivered_count | INTEGER | Successful webhook deliveries |
Permission Model
InsForge Realtime uses PostgreSQL grants and optional Row Level Security (RLS) to control channel access.RLS is disabled by default for the best developer experience. Channels and messages are open to all authenticated and anonymous users out of the box. Enable RLS when you need fine-grained access control.
Default Permissions (RLS Disabled)
| Permission | Table | Grant | Access |
|---|---|---|---|
| Subscribe | realtime.channels | SELECT | All authenticated/anon users |
| Publish | realtime.messages | INSERT | All authenticated/anon users |
Enabling Access Control
To restrict access, enable RLS and add policies:How RLS Works (When Enabled)
| Permission | Table | Policy Type | Description |
|---|---|---|---|
| Subscribe | realtime.channels | SELECT | Who can subscribe to a channel |
| Publish | realtime.messages | INSERT | Who can publish to a channel |
- When a client subscribes, the backend executes a SELECT query on
realtime.channelswith the user’s role - RLS policies filter the result. If a matching channel is returned, subscription is allowed
- When a client publishes, the backend attempts an INSERT into
realtime.messages - RLS policies evaluate the INSERT. If allowed, the message is stored and broadcast
Helper Function
Userealtime.channel_name() in your policies to access the channel being requested:
Message Flow
System Events (Database Triggers)
Client Events (User-Initiated)
Publish Function
Therealtime.publish() function is called by your database triggers:
WebSocket Events
Client-to-Server Events
| Event | Payload | Description |
|---|---|---|
realtime:subscribe | { channel: string } | Subscribe to a channel |
realtime:unsubscribe | { channel: string } | Unsubscribe from a channel |
realtime:publish | { channel, event, payload } | Publish a message |
Server-to-Client Events
| Event | Payload | Description |
|---|---|---|
{eventName} | Message payload + meta | Custom event from channel |
realtime:error | { code, message } | Error notification |
Socket Message Structure
All WebSocket messages include ameta object with server-enforced fields alongside the event payload:
Webhook Delivery
When channels havewebhook_urls configured, messages are delivered via HTTP POST:
Request Format
Delivery Guarantees
| Feature | Behavior |
|---|---|
| Retries | 2 retries with 1s, 2s backoff |
| Timeout | 10 seconds per request |
| Parallel | All webhook URLs called concurrently |
| Tracking | Success/failure counts stored per message |
Sender Types
| Type | Source | Description |
|---|---|---|
| system | Database triggers | Events from realtime.publish() function |
| user | Client SDK | Events from insforge.realtime.publish() |
Developer Workflow
1
Define Channels
Create channel patterns in
realtime.channels:2
Configure Permissions (Optional)
RLS is disabled by default, so all users can subscribe and publish. To restrict access, enable RLS and add policies:
3
Create Triggers
Add trigger function and trigger to emit events on database changes:
4
Subscribe in Client
Use the SDK to subscribe and listen:
Architecture Features
Database-Driven
Events originate from PostgreSQL triggers, ensuring consistency with your data
Optional RLS Security
Works out of the box, with optional RLS for fine-grained access control
Dual Delivery
Messages delivered to both WebSocket clients and webhook endpoints simultaneously
Audit Trail
All messages stored in database with delivery statistics for debugging and replay
Pattern Matching
Wildcard channel patterns let you define permissions for dynamic channels
Bidirectional
Clients can both receive events and publish their own messages (subject to RLS)
Performance Characteristics
| Metric | Value | Notes |
|---|---|---|
| Latency | ~10-50ms | Database to client, depends on network |
| Throughput | High | Limited by PostgreSQL NOTIFY and Socket.IO |
| Persistence | Full | All messages stored in database |
| Reconnection | Automatic | Socket.IO handles reconnection |
| Webhook Timeout | 10s | Per webhook request |
Best Practices
Keep Payloads Small
Only include necessary data in payloads
Use Specific Channels
Prefer
order:123 over broadcasting to orders for reducing the trafficAdd RLS When Needed
Enable RLS and add policies when you need to restrict channel access
Monitor Delivery
Check
ws_audience_count and wh_delivered_count to debug delivery issuesHandle Reconnection
Design clients to refetch state on reconnect since missed messages are not replayed
Use Webhooks for Reliability
For critical notifications, configure webhook URLs as backup delivery method
Limitations
- No Message Replay: Clients don’t receive messages missed during disconnection
- No Presence: No built-in tracking of who’s online in a channel
- Single Region: Messages delivered from single backend instance
- Webhook Retries: Limited to 2 retries with short timeout