Socket.IO Events
The GxP Toolkit includes a Socket.IO server for simulating real-time events during development. This allows you to test how your plugin responds to events from the platform without connecting to production systems.
Overview
The Socket.IO system consists of:
- Socket.IO Server - Node.js server that broadcasts events
- Event Templates - JSON files defining event structure
- Store Integration - Methods to listen and emit events
- CLI Tools - Commands to list and send events
Quick Start
Start the Socket Server
# Start with development server
gxdev dev --with-socket
# Or start standalone
gxdev socket
The server runs on port 3069 by default (configurable via SOCKET_IO_PORT).
Send a Test Event
# List available events
gxdev socket list
# Send an event
gxdev socket send --event AiSessionMessageCreated
Listen in Your Plugin
import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';
const store = useGxpStore();
// Listen for events on the primary socket
store.listenSocket('primary', 'AiSessionMessageCreated', (data) => {
console.log('Message received:', data);
});
Socket Server
Starting the Server
The Socket.IO server can be started in several ways:
# Via TUI command
/socket
# With Mock API
/socket --with-mock
# Via CLI
gxdev dev --with-socket
# Standalone
node server.js # (if published to project)
Server Configuration
Environment variables:
| Variable | Default | Description |
|---|---|---|
SOCKET_IO_PORT | 3069 | Socket server port |
USE_HTTPS | true | Use secure WebSocket |
CERT_PATH | SSL certificate path | |
KEY_PATH | SSL private key path |
Server Features
- CORS enabled - Accepts connections from dev server
- HTTPS support - Uses same certificates as dev server
- Event emit endpoint - HTTP endpoint for sending events
- Channel support - Events sent on specific channels
Event Templates
File Location
Event templates are JSON files in the socket-events/ directory:
socket-events/
├── AiSessionMessageCreated.json
├── SocialStreamPostCreated.json
└── SocialStreamPostVariantCompleted.json
Template Structure
{
"event": "EventName",
"channel": "private.Model.identifier",
"data": {
"id": 123,
"field1": "value1",
"field2": "value2",
"created_at": "2024-01-15T10:30:00Z"
}
}
| Field | Description |
|---|---|
event | Event name (must match what your code listens for) |
channel | Broadcasting channel (follows pattern) |
data | Event payload (any JSON structure) |
Channel Format
Channels follow the pattern:
private.{Model}.{identifier}
Examples:
private.AiInterface.ai_interface_background_removerprivate.SocialStream.social_stream_mainprivate.CheckIn.checkin_kiosk_1
Example Templates
AI Session Message
{
"event": "AiSessionMessageCreated",
"channel": "private.AiInterface.ai_interface_background_remover",
"data": {
"id": 1234,
"ai_session_id": 567,
"message": "Background removal process completed successfully",
"type": "completion",
"metadata": {
"processing_time": 2.3,
"input_image": "/dev-assets/images/product-placeholder.jpg",
"output_image": "/dev-assets/images/background-placeholder.jpg",
"confidence": 0.95
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
Social Stream Post
{
"event": "SocialStreamPostCreated",
"channel": "private.SocialStream.social_stream_main",
"data": {
"id": 789,
"social_stream_id": 101,
"content": "Just arrived at the conference! #TechConf2024",
"author": {
"name": "Jane Smith",
"avatar": "/dev-assets/images/avatar-placeholder.jpg",
"handle": "@janesmith"
},
"media": [],
"likes": 0,
"shares": 0,
"created_at": "2024-01-15T14:22:00Z"
}
}
Creating Custom Events
- Create a new JSON file in
socket-events/:
{
"event": "AttendeeCheckedIn",
"channel": "private.CheckIn.checkin_kiosk_lobby",
"data": {
"id": 456,
"attendee_id": 789,
"attendee_name": "John Doe",
"badge_number": "A-0042",
"check_in_time": "2024-01-15T09:15:00Z",
"kiosk_id": "kiosk_lobby"
}
}
- Send the event:
gxdev socket send --event AttendeeCheckedIn
Store Integration
Primary Socket
The GxP store automatically connects to the Socket.IO server:
import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';
const store = useGxpStore();
Listening for Events
Basic Listener
// Listen on primary socket
store.listenSocket('primary', 'EventName', (data) => {
console.log('Event received:', data);
});
With Dependency
For events tied to specific dependencies:
// Listen for dependency-specific events
store.useSocketListener('badge-printer', 'print-complete', (result) => {
if (result.success) {
console.log('Badge printed!');
}
});
Emitting Events
Send events from your plugin:
// Emit on primary socket
store.emitSocket('primary', 'user-action', {
action: 'button_click',
button_id: 'checkin-submit',
timestamp: new Date().toISOString()
});
State Change Listener
Listen for state change broadcasts:
const socket = store.sockets.primary;
socket.listenForStateChange((newState) => {
console.log('State changed:', newState);
store.updateState('remote_value', newState.value);
});
Dependency-Based Sockets
Configure dependencies in your manifest to set up automatic socket listeners:
{
"dependencies": [
{
"identifier": "badge_printer_1",
"model": "BadgePrinter",
"events": {
"created": "BadgePrintJobCreated",
"updated": "BadgePrintJobUpdated",
"completed": "BadgePrintJobCompleted"
}
}
]
}
The store automatically sets up listeners:
// Listeners are created automatically
store.sockets['badge_printer_1'].created.listen((data) => {
console.log('Print job created:', data);
});
store.sockets['badge_printer_1'].completed.listen((data) => {
console.log('Print job done:', data);
});
CLI Commands
List Events
gxdev socket list
Output:
📡 Available socket events:
🎯 AiSessionMessageCreated
Event: AiSessionMessageCreated
Channel: private.AiInterface.ai_interface_background_remover
🎯 SocialStreamPostCreated
Event: SocialStreamPostCreated
Channel: private.SocialStream.social_stream_main
💡 Usage:
gxdev socket send --event AiSessionMessageCreated
gxdev socket send --event SocialStreamPostCreated --identifier social_stream
Send Event
# Basic send
gxdev socket send --event EventName
# With custom identifier (updates channel)
gxdev socket send --event AttendeeCheckedIn --identifier kiosk_2
The --identifier flag updates the channel:
- Original:
private.CheckIn.checkin_kiosk_lobby - With
--identifier kiosk_2:private.CheckIn.kiosk_2
TUI Commands
In the interactive TUI:
# Start socket server
/socket
# With mock API
/socket --with-mock
# List events
/socket list
# Send event
/socket send AiSessionMessageCreated
# Send with identifier
/socket send AttendeeCheckedIn kiosk_2
In-Browser Dev Tools
The Dev Tools (Ctrl+Shift+D) include a Socket Simulator:
- Open Dev Tools → Socket Simulator tab
- Select an event from the dropdown
- Modify the JSON payload if needed
- Click Send to emit the event
The simulator:
- Shows all available events
- Allows payload editing
- Shows send confirmation
- Displays any errors
Mock API Server
Start with mock API for HTTP endpoint simulation:
gxdev dev --with-socket --with-mock
# or
/socket --with-mock
The mock API provides:
/mock-api/*endpoints- Automatic response generation
- Request logging
Real-World Usage Patterns
Check-In Flow
// Setup: Listen for check-in events
onMounted(() => {
store.listenSocket('primary', 'AttendeeCheckedIn', handleCheckIn);
});
function handleCheckIn(data) {
// Update UI with check-in data
store.updateState('last_checkin', data);
// Trigger badge print
store.emitSocket('primary', 'PrintBadge', {
attendee_id: data.attendee_id,
badge_number: data.badge_number
});
}
Social Wall
const posts = ref([]);
onMounted(() => {
// Listen for new posts
store.listenSocket('primary', 'SocialStreamPostCreated', (post) => {
posts.value.unshift(post);
});
// Listen for post updates (likes, shares)
store.listenSocket('primary', 'SocialStreamPostUpdated', (update) => {
const idx = posts.value.findIndex(p => p.id === update.id);
if (idx >= 0) {
posts.value[idx] = { ...posts.value[idx], ...update };
}
});
});
AI Processing
const processing = ref(false);
const result = ref(null);
async function startAiProcessing(imageUrl) {
processing.value = true;
// Request processing
await store.apiPost('/ai/process', { image: imageUrl });
// Listen for completion
store.listenSocket('primary', 'AiSessionMessageCreated', (data) => {
if (data.type === 'completion') {
processing.value = false;
result.value = data;
}
});
}
Troubleshooting
Socket not connecting
Error: Cannot connect to Socket.IO server
Solutions:
- Ensure server is running (
gxdev dev --with-socket) - Check port isn't blocked (
SOCKET_IO_PORT) - Verify HTTPS certificates are valid
Events not received
- Check event name matches exactly (case-sensitive)
- Verify channel matches dependency configuration
- Look for connection errors in browser console
Event file not found
Error: Event file not found: MyEvent.json
Solutions:
- Check file exists in
socket-events/ - Verify filename matches event name
- Check JSON syntax is valid
CORS errors
If you see CORS errors:
- Ensure dev server and socket server use same protocol (both HTTP or both HTTPS)
- Check socket server port is correct in environment
Server Customization
Publish the server for customization:
gxdev publish server.js
Then modify server.js in your project root to:
- Add custom endpoints
- Modify CORS settings
- Add authentication
- Custom event handling
// Example: Add custom endpoint
app.post('/custom-emit', (req, res) => {
const { event, data } = req.body;
io.emit(event, data);
res.json({ success: true });
});