socket.io-namespace-controller
v2.2.2
Published
Wrapper around Socket.IO namespaces which turn them into controller-like objects with advanced abilities
Downloads
16
Maintainers
Readme
socket.io-namespace-controller
Wrapper around Socket.IO namespaces which turn them into controller-like objects with advanced abilities
Installation
npm i socket.io-namespace-controller
Intro
Main purpose of this library is to compose socket event handlers into objects. So event handler declaration and registration are separated and logic is now structured and encapsulated. Now you can group them into mixins and reuse code across controllers. Also you can easily wrap your methods at one centralized place.
Getting started
First, you need to get setupController
function from libray, giving it socket.io
Server instance:
const io = require('socket.io')(server);
const setupController = require('socket.io-namespace-controller').driver(io);
setupController(namespace, definition)
takes two arguments:
namespace
- name of socket.io namespace, your controller shall be assigned todefinition
- object, describing your controller logic. It can consists from following properties:methods
- object of methods, which shall be assigned to same named eventsemitters
- object of methods, which shall store specific emitter logic and can be called by other controllerscreated
- hooks, which should be called after namespace createdconnected
- hook, which should be called after each socket connection to namespace
Methods
Let's create very simple controller, which shall handle two events:
load
- for settings loading by clientsupdate
- for settings updating with broadcasting of changed settings to all connected clients.
setupController('/settings', {
methods: {
async load() {
this.emit('data', await settingsService.all());
},
async update(data) {
await settingsService.set(data);
this.broadcast.emit('data', data);
}
}
})
As you can see, Socket
instance has been bound as this
to our methods.
Emitters
Let's do something more complex and useful for our beloved clients. Client, which changes settings, shall be pleased to see nice green notification on update success.
Such logic must be separated into own controller, do you agree? So let us create controller, which shall have two methods - for emit notifications to sender, and to all except sender:
setupController('/notifications', {
emitters: {
notify(message) {
this.emit('message', message);
},
notifyOthers(message) {
this.broadcast.emit('message', message);
}
}
});
and after that we can easily add usage of it into our settings
controller by editing update()
method:
async update(data) {
await settingsService.set(data);
this.as('/notifications').notify('You have successfully updated settings!');
this.as('/notifications').notifyOthers('Settings updated.');
this.broadcast.emit('data', data);
}
All events of your namespace, ability of emitting which you want to share across other controllers, must be described as part of emitters
object. Only emitters
object is returned by calling this.as()
from your controller method. You shall not have direct access to emit
or broadcast
of target controller, only through emitters
.
It done such way for having all namespace events described only in controller they belong to.
Emitters can be used at their controller methods. For example let's add such method into described above notifications
controller:
methods: {
async getUnread(clientId) {
const messages = await notificationsService.getUnreadFor(clientId);
for (let message of messages) this.emitters.notify(message);
}
}
this
Context
Each method and emitter is bound to connected socket
instance with following additional properties assigned:
as(namespaceControllerName)
- method which returns emitters of another controllermethods
- your controller methodsemitters
- your controller emitters
Hooks
created(namespace)
- is called after namespace
creation. receives original socket.io Namespace
instance.
That's place for assigning middleware to namespace, etc.
Example:
setupController('/test', {
methods: { ... },
created(namespace) {
namespace.use((socket, next) => {
if (socket.request.headers.cookie) return next();
next(new Error('Authentication error'));
})
}
})
connected(socket)
- is called after client connected, receives original socket.io Socket
instance.
That's place for room joining, etc.