Basic usage
API overview
[Server] Create a server
Similar logic should already be inside server.js
if using the default boilerplate:
// --- in server.js ---
const http = require('http');
const socketClusterServer = require('socketcluster-server');
let options = {
// ...
};
let httpServer = http.createServer();
let agServer = socketClusterServer.attach(httpServer, options);
// port 8000
httpServer.listen(8000);
The httpServer
argument must be an instance of a Node.js HTTP server.
The list of possible options
which can be passed to the SocketCluster server instance can be found here.
Note that you can also use the ECMAScript import
syntax.
The ag
in agServer
(and other related objects) stands for asynchronous generator
; it is used to differentiate the async generator approach used in new versions of SC from the callback/listener approach from previous versions.
[Server] Listen for inbound socket connections
Inside server.js
, you can find the for-await-of
loop which is handling inbound connections. It should look like this:
// --- in server.js ---
// SocketCluster/WebSocket connection handling loop.
(async () => {
for await (let {socket} of agServer.listener('connection')) {
// Handle socket connection.
}
})();
[Client] Connect to the server
Inside public/index.html
, a client connects to the server like this:
// --- in public/index.html ---
let socket = socketClusterClient.create();
^ If the connection succeeds, this will cause the connection
loop on the server side to iterate once.
!! You can pass an options
object to the socketClusterClient.create(...)
function.
[Server] Listen for inbound messages on a socket
You can use a socket.receiver(...)
within a for-await-of
loop to handle messages on a socket:
// --- in server.js ---
// SocketCluster/WebSocket connection handling loop.
(async () => {
for await (let {socket} of agServer.listener('connection')) {
(async () => {
// Set up a loop to handle remote transmitted events.
for await (let data of socket.receiver('customRemoteEvent')) {
// ...
}
})();
}
})();
!! You can also iterate over a socket.receiver(receiverName)
stream on a client socket using the same syntax.
[Client] transmit messages through a socket
// --- in public/index.html ---
// ... After the socket is created.
socket.transmit('customRemoteEvent', 123);
^ If the message reaches the server, this will cause the customRemoteEvent
loop on the server side to iterate once; data
will be 123
.
!! You can also use the same syntax to transmit from the server socket. Transmit can never fail, so you don’t need to wrap it in a try-catch
block.
[Server] Listen for inbound RPCs on a socket
Unlike messages, RPCs expect a response from the other side.
You can use a socket.procedure(...)
within a for-await-of
loop to handle RPCs on a socket:
// --- in server.js ---
// SocketCluster/WebSocket connection handling loop.
(async () => {
for await (let {socket} of agServer.listener('connection')) {
(async () => {
// Set up a loop to handle and respond to RPCs.
for await (let request of socket.procedure('customProc')) {
if (request.data && request.data.bad) {
let badCustomError = new Error('Server failed to execute the procedure');
badCustomError.name = 'BadCustomError';
request.error(badCustomError);
continue;
}
request.end('Success');
}
})();
}
})();
!! You can also iterate over a socket.procedure(procedureName)
stream on a client socket using the same syntax.
[Client] Invoke RPCs through a socket
// --- in public/index.html ---
// ... After the socket is created.
(async () => {
let result = await socket.invoke('customProc', {foo: 'bar'});
// result will be 'Success'
})();
!! You should always add a try-catch
block around the socket.invoke(...)
call to capture async errors:
// --- in public/index.html ---
// ... After the socket is created.
(async () => {
let result;
try {
result = await socket.invoke('customProc', {bad: true});
} catch (error) {
// error will throw.
// error.name will be 'BadCustomError'.
}
})();
!! You can also use the same syntax on server sockets.
[Client] Subscribe to a channel and watch for messages
(async () => {
let channel = socket.subscribe('foo');
for await (let data of channel) {
// ... Handle channel data.
}
})();
[Client] Publish to a channel without waiting for acknowledgement
// Publish data; do not wait for an acknowledgement from the server.
socket.transmitPublish('foo', 'This is some data');
!! You can also call transmitPublish
on an AGChannel
object; in this case, you should omit the first argument, for example:
let fooChannel = socket.channel('foo');
fooChannel.transmitPublish('This is some data');
[Client] Publish to a channel and wait for acknowledgement
(async () => {
try {
// Publish data; wait for an acknowledgement from the server.
await socket.invokePublish('foo', 'This is some more data');
} catch (error) {
// ... Handle potential error if server does not acknowledge before timeout.
}
})();
!! You can also call invokePublish
on an AGChannel
object; in this case, you should omit the first argument, for example:
(async () => {
let fooChannel = socket.channel('foo');
try {
// Publish data; wait for an acknowledgement from the server.
await fooChannel.invokePublish('This is some more data');
} catch (error) {
// ... Handle potential error if server does not acknowledge before timeout.
}
})();
[Server] Publish to a channel without waiting for acknowledgement
// Publish data; do not wait for an acknowledgement from the back end broker.
agServer.exchange.transmitPublish('foo', 'This is some data');
[Server] Publish to a channel and wait for acknowledgement
(async () => {
try {
// Publish data; wait for an acknowledgement from the back end broker (if it exists).
await agServer.exchange.invokePublish('foo', 'This is some more data');
} catch (error) {
// ... Handle potential error if broker does not acknowledge before timeout.
}
})();