WebSocket Realtime API
This guide is exclusively for the Socket.IO realtime interface.
Glossary
- Inbound adapter — accepts external protocol calls and translates them into use-case calls.
Responsibility in context
- Stack layer: adapter / realtime
- Owns: framework-specific wiring for this technology
- Used with: backend-template composition, persistence/SDK packages as needed, matching delivery guide
- Not responsible for: domain rules, OpenAPI authoring, or browser offline storage
Why it exists
Framework choice should stay at the edge. This adapter keeps Express/Fastify/DB/realtime details replaceable.
What it is
Websocket Api adapter for Jumentix realtime interfaces — mounts application use-cases without leaking framework types into the domain.
Scope
- Transport: WebSocket (Socket.IO protocol)
- Server implementation:
apps/backend-template/src/interface/WebSocket/WebSocketAPI.ts - SDK client:
sdk-clients/websocket/WebSocketApiClient.ts - AsyncAPI source:
spec/asyncapi/1.0.0.websocket.yml
Contract References
- WebSocket Realtime Contracts
- Error Contracts and Responses
- Events and Messages Map
Endpoint and Channels
- Base URL:
ws://localhost:3001 - Socket.IO path:
/ws - Generic request channel:
api:request - Generic response channel:
api:response - Per-operation request:
api:{operationId}:request - Per-operation response:
api:{operationId}:response
Horizontal Scaling with Redis Streams Adapter
To run multiple Socket.IO servers with consistent cross-node delivery, enable Redis Streams adapter:
JUMENTIX_WEBSOCKET_SOCKETIO_ADAPTER=redis-streams
JUMENTIX_WEBSOCKET_REDIS_URL=redis://127.0.0.1:6379/1Fallback resolution order for Redis connection:
JUMENTIX_WEBSOCKET_REDIS_URLJUMENTIX_REDIS_URLJUMENTIX_REDIS_HOST+JUMENTIX_REDIS_PORT+JUMENTIX_REDIS_DATABASE(+JUMENTIX_REDIS_PASSWORD)
Implementation files:
apps/backend-template/src/interface/WebSocket/adapters/socket-io/redisStreamsAdapter.tsapps/backend-template/src/interface/WebSocket/adapters/socket-io/socket-io.ts
Multi-thread Resilience with Socket.IO Cluster Adapter
To scale across CPU workers (multiple Node.js threads/processes in the same host), enable:
JUMENTIX_WEBSOCKET_SOCKETIO_ADAPTER=cluster
JUMENTIX_WEBSOCKET_CLUSTER_WORKERS=4Implementation files:
apps/backend-template/src/interface/WebSocket/adapters/socket-io/clusterAdapter.tsapps/backend-template/src/interface/WebSocket/adapters/start-websocket-api.tsapps/backend-template/src/interface/WebSocket/adapters/socket-io/socket-io.ts
Notes:
- Primary process forks workers and restarts dead workers automatically.
- Worker processes host Socket.IO and share events via
@socket.io/cluster-adapter. - For multi-host deployments, prefer Redis Streams adapter.
Multi-instance Validation Test
A dedicated integration test validates resilience with 2 Socket.IO servers + Redis:
apps/backend-template/test/integration/realtime/socketio.redis-streams.multi-instance.test.ts
Run it with Docker:
bun run smoke:realtime:redis-streamsOr run only the test (requires Redis running):
bun run test:integration:realtime:redis-streamsRuntime Flow
Deep Example: Generic Request + ACK correlation
import { io } from 'socket.io-client';
import { randomUUID } from 'crypto';
const socket = io('ws://localhost:3001', {
path: '/ws',
transports: ['websocket']
});
await new Promise<void>((resolve) => socket.on('connect', () => resolve()));
const requestId = randomUUID();
const channel = 'api:createOrganization:response';
socket.on(channel, (payload) => {
if (payload?.metadata?.requestId !== requestId) return;
console.log('Operation channel response:', payload);
});
socket.timeout(30000).emit(
'api:request',
{
version: '1.0.0',
operationId: 'createOrganization',
authorization: 'Bearer <jwt>',
input: {
name: 'Acme Group',
address: [],
phone: [],
email: []
},
metadata: { requestId }
},
(ackPayload) => {
console.log('ACK response:', ackPayload);
}
);Deep Example: Per-operation Channel Request
import { io } from 'socket.io-client';
const socket = io('ws://localhost:3001', {
path: '/ws',
transports: ['websocket']
});
await new Promise<void>((resolve) => socket.on('connect', () => resolve()));
socket.emit(
'api:getAllOrganizations:request',
{
version: '1.0.0',
authorization: 'Bearer <jwt>',
queryString: { page: 1, size: 20 },
metadata: { requestId: 'req-001' }
},
(response) => {
if (!response.ok) {
console.error(response.error);
return;
}
console.log(response.result);
}
);SDK Example
import { WebSocketApiClient } from '../sdk-clients/websocket/WebSocketApiClient';
const client = new WebSocketApiClient('ws://localhost:3001');
client.connect();
const response = await client.request({
version: '1.0.0',
operationId: 'getAllOrganizations',
authorization: 'Bearer <jwt>',
queryString: { page: 1, size: 10 },
metadata: { requestId: 'req-ws-01' }
});
console.log(response.result);
client.disconnect();Response/Error Handling Rules
ok=truemeansresultis the response payload for theoperationId.ok=falsemeanserrorcontains normalized error data.metadata.requestIdis the correlation key to match client requests.metadata.channelcontains the response channel used by the server.
Operational Guidance
- Always send
metadata.requestIdfrom the client. - Subscribe to both
api:responseandapi:{operationId}:responsewhen building generic clients. - Keep a client-side timeout and retry strategy for transient network failures.
Junior checklist (“I can …”)
- I know when to pick this adapter
- I can start it from the documented script
- I know the next guide/package to read
Next step
Return to Getting started or the matching delivery guide.