![PyPI Now Supports iOS and Android Wheels for Mobile Python Development](https://cdn.sanity.io/images/cgdhsj6q/production/96416c872705517a6a65ad9646ce3e7caef623a0-1024x1024.webp?w=400&fit=max&auto=format)
Security News
PyPI Now Supports iOS and Android Wheels for Mobile Python Development
PyPI now supports iOS and Android wheels, making it easier for Python developers to distribute mobile packages.
@kirills98/socket-controllers
Advanced tools
Use class-based controllers to handle websocket events
Use class-based controllers to handle websocket events. Helps to organize your code using websockets in classes.
Install module:
npm install socket-controllers --save
reflect-metadata
shim is required:
npm install reflect-metadata --save
and make sure to import it in a global place, like app.ts:
import "reflect-metadata";
ES6 features are used, if you are using old version of node.js you may need to install es6-shim:
npm install es6-shim --save
and import it in a global place like app.ts:
import "es6-shim";
Optionally you can install socket.io typings:
typings install dt~socket.io --save --global
Create a file MessageController.ts
import {OnConnect, SocketController, ConnectedSocket, OnDisconnect, MessageBody, OnMessage} from "socket-controllers";
@SocketController()
export class MessageController {
@OnConnect()
connection(@ConnectedSocket() socket: any) {
console.log("client connected");
}
@OnDisconnect()
disconnect(@ConnectedSocket() socket: any) {
console.log("client disconnected");
}
@OnMessage("save")
save(@ConnectedSocket() socket: any, @MessageBody() message: any) {
console.log("received message:", message);
console.log("setting id to the message and sending it back to the client");
message.id = 1;
socket.emit("message_saved", message);
}
}
Create a file app.ts
import "es6-shim"; // this shim is optional if you are using old version of node
import "reflect-metadata"; // this shim is required
import {createSocketServer} from "socket-controllers";
import "./MessageController"; // we need to "load" our controller before call createSocketServer. this is required
createSocketServer(3001);
Now you can send save
websocket message using webosocket-client.
Controller action marked with @OnConnect()
decorator is called once new client connected.
Controller action marked with @OnDisconnect()
decorator is called once client disconnected.
import {SocketController, OnConnect, OnDisconnect} from "socket-controllers";
@SocketController()
export class MessageController {
@OnConnect()
save() {
console.log("client connected");
}
@OnDisconnect()
save() {
console.log("client disconnected");
}
}
@ConnectedSocket()
decoratorTo get connected socket instance you need to use @ConnectedSocket()
decorator.
import {SocketController, OnMessage, ConnectedSocket} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
save(@ConnectedSocket() socket: any) {
socket.emit("save_success");
}
}
@MessageBody()
decoratorTo get received message body use @MessageBody()
decorator:
import {SocketController, OnMessage, MessageBody} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
save(@MessageBody() message: any) {
console.log("received message: ", message);
}
}
If you specify a class type to parameter that is decorated with @MessageBody()
,
socket-controllers will use class-transformer to create instance of the given class type with the data received in the message.
To disable this behaviour you need to specify a { useConstructorUtils: false }
in SocketControllerOptions when creating a server.
@SocketQueryParam()
decoratorTo get received query parameter use @SocketQueryParam()
decorator.
import {SocketController, OnMessage, MessageBody} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
save(@SocketQueryParam("token") token: string) {
console.log("authorization token from query parameter: ", token);
}
}
@SocketId()
decoratorTo get connected client id use @SocketId()
decorator.
import {SocketController, OnMessage, MessageBody} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
save(@SocketId() id: string) {
}
}
@SocketIO()
decoratorimport {SocketController, OnMessage, MessageBody} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
save(@SocketIO() io: any) {
// now you can broadcast messages to specific rooms or namespaces using io instance
}
}
You can use @EmitOnSuccess
decorator:
import {SocketController, OnMessage, EmitOnSuccess} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
@EmitOnSuccess("save_successfully")
save() {
// after this controller executed "save_successfully" message will be emitted back to the client
}
}
If you return something, it will be returned in the emitted message data:
import {SocketController, OnMessage, EmitOnSuccess} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
@EmitOnSuccess("save_successfully")
save() {
// after this controller executed "save_successfully" message will be emitted back to the client with message object
return {
id: 1,
text: "new message"
};
}
}
You can also control what message will be emitted if there is error/exception during execution:
import {SocketController, OnMessage, EmitOnSuccess, EmitOnFail} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
@EmitOnSuccess("save_successfully")
@EmitOnFail("save_error")
save() {
if (1 === 1) {
throw new Error("One is equal to one! Fatal error!");
}
return {
id: 1,
text: "new message"
};
}
}
In this case save_error
message will be sent to the client with One is equal to one! Fatal error!
error message.
Additionally, you can also control what message will be emitted for specific error conditions:
import {SocketController, OnMessage, EmitOnSuccess, EmitOnFail, EmitOnFailFor} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("save")
@EmitOnSuccess("save_successfully")
@EmitOnFailFor("id_not_found", () => NotFoundException)
@EmitOnFail("save_error")
save(id: string) {
if (id <= 0) {
throw new NotFoundException("The provided id could not be found!");
} else {
throw new Error("Could not save id!");
}
return {
id: 1,
text: "new message"
};
}
}
export class NotFoundException {
constructor(message?: string);
}
In this case id_not_found
message will be sent to the client with The provided id could not be found!
error message when the NotFoundException
was thrown.
Otherwise the client will receieve save_error
with Could not save id!
error message.
Note: If you want to use @EmitOnFailFor()
your thrown exceptions cannot inherit from the Error
class, unless you use this workaround. Not inheriting from the Error
class will also let you
differentiate between JavaScript runtime errors and your Application Exceptions.
Sometimes you may want to not emit success/error message if returned result is null or undefined.
In such cases you can use @SkipEmitOnEmptyResult()
decorator.
import {SocketController, OnMessage, EmitOnSuccess, EmitOnFail, SkipEmitOnEmptyResult} from "socket-controllers";
@SocketController()
export class MessageController {
@OnMessage("get")
@EmitOnSuccess("get_success")
@SkipEmitOnEmptyResult()
get(): Promise<Message[]> {
return this.messageRepository.findAll();
}
}
In this case if findAll will return undefined, get_success
message will not be emitted.
If findAll will return array of messages, they will be emitted back to the client in the get_success
message.
This example also demonstrates Promises support.
If promise returned by controller action, message will be emitted only after promise will be resolved.
If you need to create and configure socket.io server manually,
you can use useSocketServer
instead of createSocketServer
function.
Here is example of creating socket.io server and configuring it with express:
import "reflect-metadata"; // this shim is required
import {useSocketServer} from "socket-controllers";
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);
server.listen(3001);
app.get("/", function (req: any, res: any) {
res.send("hello express");
});
io.use((socket: any, next: Function) => {
console.log("Custom middleware");
next();
});
useSocketServer(io);
You can load all controllers in once from specific directories, by specifying array of directories via options in
createSocketServer
or useSocketServer
:
import "reflect-metadata"; // this shim is required
import {createSocketServer, loadControllers} from "socket-controllers";
createSocketServer(3000, {
controllers: [__dirname + "/controllers/*.js"]
}); // registers all given controllers
To listen to messages only of the specific namespace you can mark a controller with namespace:
@SocketController("/messages")
export class MessageController {
// ...
}
Middlewares are the functions passed to the socketIo.use
method.
Middlewares allows you to define a logic that will be executed each time client connected to the server.
To create your middlewares use @Middleware
decorator:
import {Middleware, MiddlewareInterface} from "socket-controllers";
@Middleware()
export class CompressionMiddleware implements MiddlewareInterface {
use(socket: any, next: ((err?: any) => any)) {
console.log("do something, for example get authorization token and check authorization");
next();
}
}
Controllers and middlewares should be loaded globally, before app bootstrap:
import "reflect-metadata";
import {createSocketServer} from "socket-controllers";
import "./MessageController";
import "./MyMiddleware"; // here we load it
let io = createSocketServer(3000);
Also you can load them from directories. Also you can use glob patterns:
import "reflect-metadata";
import {createSocketServer, loadControllers} from "socket-controllers";
let io = createSocketServer(3000, {
controllers: [__dirname + "/controllers/**/*.js"],
middlewareDirs: [__dirname + "/middlewares/**/*.js"]
});
socket-controllers
supports a DI container out of the box. You can inject your services into your controllers and
middlewares. Container must be setup during application bootstrap.
Here is example how to integrate socket-controllers with typedi:
import "reflect-metadata";
import {createSocketServer, useContainer} from "socket-controllers";
import {Container} from "typedi";
// its important to set container before any operation you do with socket-controllers,
// including importing controllers
useContainer(Container);
// create and run socket server
let io = createSocketServer(3000, {
controllers: [__dirname + "/controllers/*.js"],
middlewareDirs: [__dirname + "/middlewares/*.js"]
});
That's it, now you can inject your services into your controllers:
@SocketController()
export class MessageController {
constructor(private messageRepository: MessageRepository) {
}
// ... controller actions
}
Signature | Description |
---|---|
@SocketController(namespace?: string) | Registers a class to be a socket controller that can listen to websocket events and respond to them. |
@OnMessage(messageName: string) | Registers controller's action to be executed when socket receives message with given name. |
@OnConnect() | Registers controller's action to be executed when client connects to the socket. |
@OnDisconnect() | Registers controller's action to be executed when client disconnects from the socket. |
@ConnectedSocket() | Injects connected client's socket object to the controller action. |
@SocketIO() | Injects socket.io object that initialized a connection. |
@MessageBody() | Injects received message body. |
@SocketQueryParam(paramName: string) | Injects query parameter from the received socket request. |
@SocketId() | Injects socket id from the received request. |
@SocketRequest() | Injects request object received by socket. |
@SocketRooms() | Injects rooms of the connected socket client. |
@Middleware() | Registers a new middleware to be registered in the socket.io. |
@EmitOnSuccess(messageName: string) | If this decorator is set then after controller action will emit message with the given name after action execution. It will emit message only if controller succeed without errors. If result is a Promise then it will wait until promise is resolved and emit a message. |
@EmitOnFail(messageName: string) | If this decorator is set then after controller action will emit message with the given name after action execution. It will emit message only if controller throw an exception. If result is a Promise then it will wait until promise throw an error and emit a message. |
@EmitOnFailFor(messageName: string, errorType: () => ObjectType) | If this decorator is set then after controller action will emit message with the given name after action execution. It will emit message only if controller throw an exception of the specified type. If result is a Promise then it will wait until promise throw an error and emit a message. |
@SkipEmitOnEmptyResult() | Used in conjunction with @EmitOnSuccess and @EmitOnFail decorators. If result returned by controller action is null or undefined then messages will not be emitted by @EmitOnSuccess or @EmitOnFail decorators. |
Take a look on samples in ./sample for more examples of usage.
FAQs
Use class-based controllers to handle websocket events
We found that @kirills98/socket-controllers demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports iOS and Android wheels, making it easier for Python developers to distribute mobile packages.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.