@admandev/socketio-decorator
v1.1.3
Published
This library allows you to use Socket.io with TypeScript decorators, simplifying the integration and usage of Socket.io in a TypeScript environment.
Downloads
19
Maintainers
Readme
Socketio Decorator
This library allows you to use Socket.io with TypeScript decorators, simplifying the integration and usage of Socket.io in a TypeScript environment.
Table of Contents
Installation
To get started, follow these steps:
Install the package:
npm install @admandev/socketio-decorator
Install the required peer dependencies for Socket.io:
npm install socket.io
Update your
tsconfig.json
to enable decorators:{ "compilerOptions": { "module": "Node16", "experimentalDecorators": true, "emitDecoratorMetadata": true // If you use data validation } }
Usage
Create a Socket Controller
Create a file named
SocketController.ts
with the following content:import { ServerOn, SocketOn, SocketEmitter } from "@admandev/socketio-decorator"; import { Socket } from "socket.io"; export class SocketController { @ServerOn("connection") public onConnection(socket: Socket) { console.log("Socket connected with socket id", socket.id); } @SocketOn("message") public onMessage(socket: Socket, data: any) { console.log("Message received:", data); } // Async / Await is supported @SocketOn("hello") @SocketEmitter("hello-back") public async onHello() { await new Promise((resolve) => setTimeout(resolve, 2000)) return { message: "Hello you" } } }
The
SocketController
class contains 3 methods:onConnection
,onMessage
andonHello
.The
onConnection
method listens for the socket connection event. TheonMessage
method listens for amessage
event and logs the received data. TheonHello
method listens for ahello
event, waits for 2 seconds, and emits ahello-back
event with the message "Hello you".Set Up the Server
Create a file named
app.ts
with the following content:import { useSocketIoDecorator } from "@admandev/socketio-decorator"; import express from "express"; import http from "http"; import { Server } from "socket.io"; import { SocketController } from "./SocketController"; const app = express(); const server = http.createServer(app); const io = new Server(server); useSocketIoDecorator({ ioserver: io, controllers: [SocketController], }); server.listen(3000, () => { console.log("Server running on port 3000"); });
Run the Server
Start the server by running:
node dist/app.js
Ensure you have compiled your TypeScript files into JavaScript using
tsc
.You can now test the server by connecting with Postman or another WebSocket client and sending a
message
event. You should see the message logged in the console.
Decorators
Listening for Events
| Decorator | Description | Equivalent in Basic Socket.io |
|-------------------------|----------------------------------------------------------|-------------------------------------|
| @ServerOn(event: string)
| Listens for server events. | io.on(event, callback)
|
| @SocketOn(event: string)
| Listens for events emitted by the client. | socket.on(event, callback)
|
| @SocketOnce(event: string)
| Listens for events emitted by the client only once. | socket.once(event, callback)
|
| @SocketOnAny()
| Listens for any event emitted by the client. | socket.onAny(callback)
|
| @SocketOnAnyOutgoing()
| Listens for any outgoing event emitted by the client. | socket.onAnyOutgoing(callback)
|
Emitting Events
| Decorator | Description | Equivalent in Basic Socket.io |
|-------------------------|---------------------------------------------------------|--------------------------------------|
| @ServerEmitter(event?: string, to?: string)
| Emits events from the server. | io.emit(event, data)
|
| @SocketEmitter(event:? string)
| Emits events from the client socket. | socket.emit(event, data)
|
How to use
Basic Usage
The return value of the method is sent as the data of the event.
@SocketEmitter("message") public sendMessage() { return { message: "Hello, world!" } }
The above code will emit a
message
event with the following data:{ "message": "Hello, world!" }
Emitting options
You can also specify options for the emitted event by returning an
EmitOption
object.import { EmitterOption, SocketEmitter } from "@admandev/socketio-decorator" import { Socket } from "socket.io" @SocketEmitter() // No event name specified public sendMessage(socket: Socket): EmitOptions { const isAllowedToSend = isUserAllowedToSendMessage(socket) return new EmitterOption({ to: "room1", message: "newMessage", data: { message: "Hello, world!" }, disableEmit: !isAllowedToSend, }) }
The above code will emit a
newMessage
event to theroom1
room. The event will only be emitted if theisUserAllowedToSendMessage
function returnstrue
.
Emit optionsThe
EmitOption
object has the following properties:| Property | Type | Required | Description | |----------|------|----------|-------------| |
to
| string | No (if the decorator provides this) | The target to emit the event to. | |message
| string | No (if the decorator provides this) | The event name to emit. | |data
| any | Yes | The data to emit. | |disableEmit
| boolean | No | Iftrue
, the event will not be emitted. |Emitting falsy value
If the method returns a falsy value (false, null undefined, 0, ...), the event will not be emitted.
Middlewares
You can use middlewares to execute code before an event is handled. Middlewares can be used to perform tasks such as authentication or logging.
Server Middleware
A Server Middleware is executed for each incoming connection.
Create a Middleware
Create a file named
MyServerMiddleware.ts
and create a class that implements theIServerMiddleware
interface:export class MyServerMiddleware implements IServerMiddleware { use(socket: Socket, next: (err?: unknown) => void) { console.log("You can perform tasks here before the event is handled") next() } }
The
use
method is called before any event is handled. You can perform any tasks here and callnext()
to proceed with the event handling.Register the Middleware
Update the
app.ts
file to register the middleware:useSocketIoDecorator({ ..., serverMiddlewares: [MyServerMiddleware], // Add the middleware here })
Socket Middleware
A Socket Middleware is like Server Middleware but it is called for each incoming packet.
Create a Middleware
Create a file named
MySocketMiddleware.ts
and create a class that implements theISocketMiddleware
interface:import { ISocketMiddleware } from "@admandev/socketio-decorator" import { Event } from "socket.io" export class MySocketMiddleware implements ISocketMiddleware { use([event, ...args]: Event, next: (err?: Error) => void): void { console.log(`MySocketMiddleware triggered from ${event} event`) next() } }
Register the Middleware
Update the
app.ts
file to register the middleware:useSocketIoDecorator({ ..., socketMiddlewares: [MySocketMiddleware], // Add the middleware here })
Error handling middleware
You can create a middleware to handle errors that occur during event handling and above middlewares.
Create an Error Middleware
Create a file named
MyErrorMiddleware.ts
and create a class that implements theIErrorMiddleware
interface:import { IErrorMiddleware } from "@admandev/socketio-decorator"; import { Socket } from "socket.io"; export class MyErrorMiddleware implements IErrorMiddleware{ handleError (error: any, socket?: Socket) { // Handle the error here console.log('Error middleware: ', error); } }
Register the Middleware
Update the
app.ts
file to register the middleware:useSocketIoDecorator({ ..., errorMiddleware: MyErrorMiddleware, // Add the unique error middleware here })
Data validation
You can use the class-validator
library to validate the data received from the client and be sure that required fields are present and have the correct type.
Setup
Install the following libraries
npm install class-validator class-transformer reflect-metadata
Import the
reflect-metadata
libraryAdd the following line at the top of your
app.ts
file:import "reflect-metadata"
Be sure to enable the
emitDecoratorMetadata
option in yourtsconfig.json
file{ "compilerOptions": { "emitDecoratorMetadata": true } }
Enable the validation option in the
useSocketIoDecorator
configuseSocketIoDecorator({ ..., dataValidationEnabled: true })
Create and use a class with validation rules
import { IsString } from "class-validator" export class MessageData { @IsString() message: string }
Use the class in the event handler:
@SocketOn("message") public onMessage(socket: Socket, data: MessageData) { console.log("Message received:", data.message) }
If the data does not match the validation rules, an error will be thrown before the event handler is called.
[!WARNING] We recommend using the error handling middleware to catch and handle validation errors.
Disable validation for a specific handler
You can disable validation for a specific handler by setting the disableDataValidation
option to true
:
@SocketOn("message", { disableDataValidation: true })
public onMessage(socket: Socket, data: MessageData) {
...
}
Default enabled validation
Data validation works only on socket listeners (not server listeners or emitters).
Here is the default value for the disableDataValidation
option:
@SocketOn
-false
@SocketOnce
-false
@SocketOnAny
-true
- If you want to validate the data, you need to set the option tofalse
@SocketOnAnyOutgoing
-true
because it is not an incoming event from the client
Learn more about data validation
For more information on data validation, see the class-validator documentation.
Hooks
Hooks in Socketio Decorator are functions that provides some data.
UseIoServer hook
The useIoServer
is the simpliest hook that provides the io
socketio server object.
import { useIoServer } from "@admandev/socketio-decorator"
import { Server } from "socket.io"
const io: Server = useIoServer()
UseCurrentUser hook
The useCurrentUser
hook provides the current user object. This hook is useful when you want to get the current user object in the event handler.
Create the
currentUserProvider
In the
app.ts
file, create a function that returns the current user object:useSocketIoDecorator({ ..., currentUserProvider: (socket: Socket) => { const token = socket.handshake.auth.token const user = userServices.getUserByToken(token) return user }, })
Use the
useCurrentUser
hookIn the event handler, use the
useCurrentUser
hook to get the current user object:import { useCurrentUser, SocketOn } from "@admandev/socketio-decorator" import { Socket } from "socket.io" @SocketOn("message") public onMessage(socket: Socket, data: any) { const user = useCurrentUser(socket) console.log("Message received from user:", user) }
Dependency Injection
Socketio Decorator supports dependency injection using a DI library. You can inject services into your controllers and middlewares.
To allow Socketio Decorator to work with your DI system, you need to provide the Container
object to the useSocketIoDecorator
options.
import { Container } from "typedi"
useSocketIoDecorator({
...,
iocContainer: Container,
})
Note: Your Container object must provide the get
method to resolve dependencies.
Note 2: Your controllers and middlewares must be registered in the DI container.
Important: The iocContainer
option is optional. If you don't provide it, Socketio Decorator will create a new instance of the controllers or middlewares and keep them in memory.
Sample project
You can find a sample project using express here.
Thanks
Thank you for using Socketio Decorator. If you have any questions or suggestions, feel free to open an issue on the GitHub repository.