{"id":13881011,"url":"https://github.com/typestack/socket-controllers","last_synced_at":"2025-05-16T04:04:01.222Z","repository":{"id":10458617,"uuid":"65816878","full_name":"typestack/socket-controllers","owner":"typestack","description":"Use class-based controllers to handle websocket events.","archived":false,"fork":false,"pushed_at":"2025-04-29T08:37:56.000Z","size":2480,"stargazers_count":346,"open_issues_count":11,"forks_count":52,"subscribers_count":8,"default_branch":"develop","last_synced_at":"2025-05-03T02:22:37.173Z","etag":null,"topics":["decorators","socket","socket-client","socket-controllers","socket-io","socket-server","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/typestack.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-16T12:06:48.000Z","updated_at":"2025-03-16T07:51:12.000Z","dependencies_parsed_at":"2023-01-17T00:01:26.112Z","dependency_job_id":"38021c8a-aca5-446f-b422-d52f68f59d94","html_url":"https://github.com/typestack/socket-controllers","commit_stats":{"total_commits":570,"total_committers":9,"mean_commits":"63.333333333333336","dds":"0.17719298245614035","last_synced_commit":"3d64ae02d143835e15080fee4f6c0354d06a4c89"},"previous_names":["pleerock/socket-controllers"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typestack%2Fsocket-controllers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typestack%2Fsocket-controllers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typestack%2Fsocket-controllers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typestack%2Fsocket-controllers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/typestack","download_url":"https://codeload.github.com/typestack/socket-controllers/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464891,"owners_count":22075570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decorators","socket","socket-client","socket-controllers","socket-io","socket-server","typescript"],"created_at":"2024-08-06T08:03:46.720Z","updated_at":"2025-05-16T04:04:01.203Z","avatar_url":"https://github.com/typestack.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# socket-controllers\n\n![Build Status](https://github.com/typestack/socket-controllers/workflows/CI/badge.svg)\n[![codecov](https://codecov.io/gh/typestack/socket-controllers/branch/develop/graph/badge.svg)](https://codecov.io/gh/typestack/socket-controllers)\n[![npm version](https://badge.fury.io/js/socket-controllers.svg)](https://badge.fury.io/js/socket-controllers)\n\nUse class-based controllers to handle websocket events. Helps to organize your code using websockets in classes.\n\n## Installation\n\n1. Install `socket-controllers`:\n\n   ```\n   npm install socket-controllers\n   ```\n\n2. Install `reflect-metadata` shim:\n\n   ```\n   npm install reflect-metadata\n   ```\n\n   and make sure to import it in a global place, like app.ts:\n\n   ```typescript\n   import 'reflect-metadata';\n   ```\n   \n3. Install a DI container, for example `typedi`;\n   \n   ```\n   npm install typedi\n   ```\n\n## Example of usage\n\n1. Create a file `MessageController.ts`\n\n   ```typescript\n   import {\n     OnConnect,\n     SocketController,\n     ConnectedSocket,\n     OnDisconnect,\n     MessageBody,\n     OnMessage,\n   } from 'socket-controllers';\n   import {Service} from 'typedi'; // Only if you are using typedi\n\n   @SocketController()\n   @Service() // Only if you are using typedi\n   export class MessageController {\n     @OnConnect()\n     connection(@ConnectedSocket() socket: any) {\n       console.log('client connected');\n     }\n\n     @OnDisconnect()\n     disconnect(@ConnectedSocket() socket: any) {\n       console.log('client disconnected');\n     }\n\n     @OnMessage('save')\n     save(@ConnectedSocket() socket: any, @MessageBody() message: any) {\n       console.log('received message:', message);\n       console.log('setting id to the message and sending it back to the client');\n       message.id = 1;\n       socket.emit('message_saved', message);\n     }\n   }\n   ```\n\n2. Create a file `app.ts`\n\n   ```typescript\n   import 'es6-shim'; // this shim is optional if you are using old version of node\n   import 'reflect-metadata'; // this shim is required\n   import { SocketControllers } from 'socket-controllers';\n   import { MessageController } from './MessageController'; \n   import {Container} from 'typedi'; // Only if you are using typedi\n\n   new SocketControllers({\n     port: 3001,\n     container: Container,\n     controllers: [MessageController],\n   });\n   ```\n\n3. Now you can send `save` websocket message using websocket-client.\n\n## More usage examples\n\n#### Run code on socket client connect / disconnect / disconnecting\n\nController action marked with `@OnConnect()` decorator is called once new client connected.\nController action marked with `@OnDisconnect()` decorator is called once client disconnected.\nController action marked with `@OnDisconnecting()` decorator is called when the client is disconnecting, before the disconnect event.\n\n```typescript\nimport { SocketController, OnConnect, OnDisconnect, OnDisconnecting } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnConnect()\n  save() {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  save() {\n    console.log('client disconnected');\n  }\n\n  @OnDisconnecting()\n  save() {\n    console.log('client is disconnecting');\n  }\n}\n```\n\n#### `@ConnectedSocket()` decorator\n\nTo get connected socket instance you need to use `@ConnectedSocket()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, ConnectedSocket } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@ConnectedSocket() socket: any) {\n    socket.emit('save_success');\n  }\n}\n```\n\n#### `@MessageBody()` decorator\n\nTo get received message body use `@MessageBody()` decorator:\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@MessageBody() message: any) {\n    console.log('received message: ', message);\n  }\n}\n```\n\nIf you specify a class type to parameter that is decorated with `@MessageBody()`,\nsocket-controllers will use [class-transformer][1] to create instance of the given class type with the data received in the message.\nTo disable this behaviour you need to specify `{ transformOption: { transform: false ] }` in SocketControllerOptions when creating a server.\n\nYou can define an index to get multiple parameters from the socket event.\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@MessageBody({index: 0}) param1: any, @MessageBody({index: 1}) param2: any) {\n    console.log('received message: ', message1);\n    console.log('received message: ', message2);\n  }\n}\n```\n\n#### `@MessageAck()` decorator\n\nTo get received message ack use `@MessageAck()` decorator:\n\n```typescript\nimport { SocketController, OnMessage, MessageAck, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@MessageBody() message: any, @MessageAck() ack: Function) {\n    console.log('received message: ', message);\n    ack('callback message');\n  }\n}\n```\n\n\u003e note: ack must be the last parameter in `emit`, otherwise it will be `null`\n\n#### `@SocketQueryParam()` decorator\n\nTo get received query parameter use `@SocketQueryParam()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@SocketQueryParam('token') token: string) {\n    console.log('authorization token from query parameter: ', token);\n  }\n}\n```\n\n#### Get socket client id using `@SocketId()` decorator\n\nTo get connected client id use `@SocketId()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@SocketId() id: string) {}\n}\n```\n\n#### Get access to using socket.io instance using `@SocketIO()` decorator\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@SocketIO() io: any) {\n    // now you can broadcast messages to specific rooms or namespaces using io instance\n  }\n}\n```\n\n#### Send message back to client after method execution\n\nYou can use `@EmitOnSuccess` decorator:\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  @EmitOnSuccess('save_successfully')\n  save() {\n    // after this controller executed \"save_successfully\" message will be emitted back to the client\n  }\n}\n```\n\nIf you return something, it will be returned in the emitted message data:\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  @EmitOnSuccess('save_successfully')\n  save() {\n    // after this controller executed \"save_successfully\" message will be emitted back to the client with message object\n    return {\n      id: 1,\n      text: 'new message',\n    };\n  }\n}\n```\n\nYou can also control what message will be emitted if there is error/exception during execution:\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess, EmitOnFail } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  @EmitOnSuccess('save_successfully')\n  @EmitOnFail('save_error_range', {errorType: RangeError})\n  @EmitOnFail('save_error_type', {errorType: TypeError})\n  @EmitOnFail('save_error')\n  save() {\n    if (1 === 1) {\n      throw new Error('One is equal to one! Fatal error!');\n    }\n    return {\n      id: 1,\n      text: 'new message',\n    };\n  }\n}\n```\n\nIn this case `save_error` message will be sent to the client with `One is equal to one! Fatal error!` error message.\nThe order is important when defining multiple `@EmitOnFail()` decorators, the first matching errorType will be served\n\nSometimes you may want to not emit success/error message if returned result is null or undefined.\nIn such cases you can use `@SkipEmitOnEmptyResult()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess, EmitOnFail, SkipEmitOnEmptyResult } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('get')\n  @EmitOnSuccess('get_success')\n  @SkipEmitOnEmptyResult()\n  get(): Promise\u003cMessage[]\u003e {\n    return this.messageRepository.findAll();\n  }\n}\n```\n\nIn this case if findAll will return undefined, `get_success` message will not be emitted.\nIf findAll will return array of messages, they will be emitted back to the client in the `get_success` message.\nThis example also demonstrates Promises support.\nIf promise returned by controller action, message will be emitted only after promise will be resolved.\n\n#### Using exist server instead of creating a new one\n\nIf you need to create and configure socket.io server manually,\nyou can pass it to the `SocketControllers` constructor.\nHere is example of creating socket.io server and configuring it with express:\n\n```typescript\nimport 'reflect-metadata'; // this shim is required\nimport { SocketControllers } from 'socket-controllers';\nimport { Server } from 'socket.io';\nimport { Container } from 'typedi'; // Only if you are using typedi\n\nconst app = require('express')();\nconst server = require('http').Server(app);\nconst io = new Server(server);\n\nserver.listen(3001);\n\napp.get('/', function (req: any, res: any) {\n  res.send('hello express');\n});\n\nio.use((socket: any, next: Function) =\u003e {\n  console.log('Custom middleware');\n  next();\n});\nnew SocketControllers({io, container: Container});\n```\n\n#### Load all controllers from the given directory\n\nYou can load all controllers in once from specific directories, by specifying array of directories via options in\n`createSocketServer` or `useSocketServer`:\n\n```typescript\nimport 'reflect-metadata'; // this shim is required\nimport { SocketControllers } from 'socket-controllers';\nimport { Container } from 'typedi'; // Only if you are using typedi\n\nnew SocketControllers({\n  port: 3000,\n  container: Container, \n  controllers: [__dirname + '/controllers/*.js'],\n}); // registers all given controllers\n```\n\n#### Using socket.io namespaces\n\nTo listen to messages only of the specific namespace you can mark a controller with namespace:\n\n```typescript\n@SocketController('/messages')\nexport class MessageController {\n  // ...\n}\n```\n\nAlso you can use dynamic namespace, like `express router` patterns:\n\n```typescript\n@SocketController('/messages/:userId')\nexport class MessageController {\n  // ...\n}\n```\n\n## Using middlewares\n\nMiddlewares are the functions passed to the `socketIo.use` method.\nMiddlewares allows you to define a logic that will be executed each time client connected to the server.\nTo create your middlewares use `@Middleware` decorator:\n\n```typescript\nimport { Middleware, MiddlewareInterface } from 'socket-controllers';\n\n@Middleware()\nexport class CompressionMiddleware implements MiddlewareInterface {\n  use(socket: any, next: (err?: any) =\u003e any) {\n    console.log('do something, for example get authorization token and check authorization');\n    next();\n  }\n}\n```\n\nYou can limit middlewares to namespaces providing either a `string`, `RegExp` or `Array\u003cstring | RegExp\u003e` to the `namespace` parameter:\n\n```typescript\nimport { Middleware, MiddlewareInterface } from 'socket-controllers';\n\n@Middleware({namespace: '/test'})\nexport class CompressionMiddleware implements MiddlewareInterface {\n  use(socket: any, next: (err?: any) =\u003e any) {\n    console.log('do something, for example get authorization token and check authorization');\n    next();\n  }\n}\n```\n\n## Don't forget to load your controllers and middlewares\n\nControllers and middlewares should be loaded:\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers } from 'socket-controllers';\nimport { MessageController } from './MessageController';\nimport { MyMiddleware } from './MyMiddleware'; // here we import it\nimport { Container } from 'typedi'; // Only if you are using typedi\nconst server = new SocketControllers({\n  port: 3000,\n  container: Container,\n  controllers: [MessageController],\n  middlewares: [MyMiddleware],\n});\n```\n\nAlso you can load them from directories. Also you can use glob patterns:\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers } from 'socket-controllers';\nimport { Container } from 'typedi'; // Only if you are using typedi\nconst server = new SocketControllers({\n   port: 3000,\n   container: Container,\n   controllers: [__dirname + '/controllers/**/*.js'],\n   middlewares: [__dirname + '/middlewares/**/*.js'],\n});\n```\n\n## Using DI container\n\n`socket-controllers` supports a DI container out of the box. You can inject your services into your controllers and\nmiddlewares. Container must be setup during application bootstrap.\nHere is example how to integrate socket-controllers with [typedi](https://github.com/pleerock/typedi):\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers } from 'socket-controllers';\nimport { Container } from 'typedi';\n\n// create and run socket server\nconst server = new SocketControllers({\n  port: 3000,\n  container: Container,\n  controllers: [__dirname + '/controllers/*.js'],\n  middlewares: [__dirname + '/middlewares/*.js'],\n});\n```\n\nThat's it, now you can inject your services into your controllers:\n\n```typescript\n@Service()\n@SocketController()\nexport class MessageController {\n  constructor(private messageRepository: MessageRepository) {}\n\n  // ... controller actions\n}\n```\n\n\u003e Note: TypeDI won't create instances for unknown classes since 0.9.0, you have to decorate your Class as a `Service()` as well.\n\n### Scoped controllers\n\nYou can enable scoped controllers by providing a `scopedContainerGetter` function in SocketServerOptions. This function should return a new container that will be used to instantiate the controller and its dependencies.\n\nYou will get a new instance for each event in the controller.\n\nThe `scopedContainerGetter` function receives the `SocketEventContext`.\n\nThe `scopedContainerDisposer` function receives the container instance you created with `scopedContainerGetter` after the socket action is finished. Use this function to dispose the container if needed.\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers, SocketEventContext } from 'socket-controllers';\nimport { Container, ContainerInstance, Token } from \"typedi\";\n\nconst myDiToken = new Token();\n\n// create and run socket server\nconst server = new SocketControllers({\n   port: 3000,\n   container: Container,\n   scopedContainerGetter: (args: SocketEventContext) =\u003e {\n      const container = Container.of(YOUR_REQUEST_CONTEXT);\n      container.set(myDiToken, 'MY_VALUE');\n      return container;\n   },\n   scopedContainerDisposer: (container: ContainerInstance) =\u003e {\n     container.dispose();\n   },\n   controllers: [__dirname + '/controllers/*.js'],\n   middlewares: [__dirname + '/middlewares/*.js'],\n});\n```\n\n## Interceptors\n\nInterceptors allow you to wrap your event handlers in higher order functions.\nWith interceptors you can add logging or modify the incoming or outgoing data for event handlers.\n\n```typescript\nimport {\n   SocketController,\n   OnMessage,\n   EmitOnSuccess,\n   EmitOnFail,\n   SkipEmitOnEmptyResult,\n   UseInterceptor,\n   MessageBody\n} from 'socket-controllers';\n\nconst interceptor: InterceptorInterface = {\n   use: (ctx: SocketEventContext, next: () =\u003e any) =\u003e {\n     ctx.messageArgs[0] = 'modified message from controller - ' + ctx.messageArgs[0];\n     const resp = next();\n     return 'modified response from controller - ' + resp; // modified response from controller - modified response from method - reponse\n   },\n};\n\n@Service()\nclass Interceptor implements InterceptorInterface {\n   async use(ctx: SocketEventContext, next: () =\u003e any) {\n     ctx.messageArgs[0] = 'modified message from method - ' + ctx.messageArgs[0];\n     const resp = await next();\n     return 'modified response from method - ' + resp; // modified response from method - reponse\n   }\n}\n\n@SocketController()\n@UseInterceptor(interceptor)\nexport class MessageController {\n   @OnMessage('get')\n   @EmitOnSuccess('get_success')\n   @SkipEmitOnEmptyResult()\n   @UseInterceptor(Interceptor)\n   get(@MessageBody() message: string): Promise\u003cMessage[]\u003e {\n     console.log(message); // modified message from controller - modified message from method - original message\n     return 'response';\n   }\n}\n```\n\nInterceptors are executed in order of definition, starting with the controller interceptors.\n\n\n## Decorators Reference\n\n| Signature                              | Description                                                                                                                                                                                                                                                                |\n|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `@SocketController(namespace?: string\\ | Regex)`                                                                                                                                                                                                                                                                    | Registers a class to be a socket controller that can listen to websocket events and respond to them.                                                                                                                                                                       |\n| `@OnMessage(messageName: string)`      | Registers controller's action to be executed when socket receives message with given name.                                                                                                                                                                                 |\n| `@OnConnect()`                         | Registers controller's action to be executed when client connects to the socket.                                                                                                                                                                                           |\n| `@OnDisconnect()`                      | Registers controller's action to be executed when client disconnects from the socket.                                                                                                                                                                                      |\n| `@OnDisconnecting()`                   | Registers controller's action to be executed when client is disconnecting from the socket.                                                                                                                                                                                 |\n| `@ConnectedSocket()`                   | Injects connected client's socket object to the controller action.                                                                                                                                                                                                         |\n| `@SocketIO()`                          | Injects socket.io object that initialized a connection.                                                                                                                                                                                                                    |\n| `@MessageBody()`                       | Injects received message body.                                                                                                                                                                                                                                             |\n| `@SocketQueryParam(paramName: string)` | Injects query parameter from the received socket request.                                                                                                                                                                                                                  |\n| `@SocketId()`                          | Injects socket id from the received request.                                                                                                                                                                                                                               |\n| `@SocketRequest()`                     | Injects request object received by socket.                                                                                                                                                                                                                                 |\n| `@SocketRooms()`                       | Injects rooms of the connected socket client.                                                                                                                                                                                                                              |\n| `@NspParams()`                         | Injects dynamic namespace params.                                                                                                                                                                                                                                          |\n| `@NspParam(paramName: string)`         | Injects param from the dynamic namespace.                                                                                                                                                                                                                                  |\n| `@Middleware()`                        | Registers a new middleware to be registered in the socket.io.                                                                                                                                                                                                              |\n| `@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. |\n| `@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.  |\n| `@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.                                                            |     |\n\n## Samples\n\nTake a look on samples in [./sample](https://github.com/pleerock/socket-controllers/tree/master/sample) for more examples\nof usage.\n\n## Related projects\n\n- If you are interested to create controller-based express or koa server use [routing-controllers](https://github.com/pleerock/routing-controllers) module.\n- If you need to use dependency injection in use [typedi](https://github.com/pleerock/typedi) module.\n\n[1]: https://github.com/pleerock/class-transformer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftypestack%2Fsocket-controllers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftypestack%2Fsocket-controllers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftypestack%2Fsocket-controllers/lists"}