typestack/socket-controllers
Trên GitHub, typestack/socket-controllers đã đạt 342 sao, nhóm Frontend, ngôn ngữ TypeScript. Use class-based controllers to handle websocket events.
Tóm tắt dựng từ metadata GitHub của chính dự án — chưa có bài review TopGit. Trang sẽ tự động cập nhật khi bài review đầy đủ được xuất bản.
TopGit viết bài đầy đủ cho repo có nhiều sao nhất và được yêu cầu nhiều nhất. Trang này là snapshot trong thời gian chờ — xem README gốc ở tab READ ME.
Snapshot
Cộng tác viên hàng đầu
Xem cộng tác viên hàng đầu
socket-controllers
Use class-based controllers to handle websocket events. Helps to organize your code using websockets in classes.
Installation
-
Install
socket-controllers:npm install socket-controllers -
Install
reflect-metadatashim:npm install reflect-metadataand make sure to import it in a global place, like app.ts:
import 'reflect-metadata'; -
Install a DI container, for example
typedi;npm install typedi
Example of usage
-
Create a file
MessageController.tsimport { OnConnect, SocketController, ConnectedSocket, OnDisconnect, MessageBody, OnMessage, } from 'socket-controllers'; import {Service} from 'typedi'; // Only if you are using typedi @SocketController() @Service() // Only if you are using typedi 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.tsimport 'es6-shim'; // this shim is optional if you are using old version of node import 'reflect-metadata'; // this shim is required import { SocketControllers } from 'socket-controllers'; import { MessageController } from './MessageController'; import {Container} from 'typedi'; // Only if you are using typedi new SocketControllers({ port: 3001, container: Container, controllers: [MessageController], }); -
Now you can send
savewebsocket message using websocket-client.
More usage examples
Run code on socket client connect / disconnect / disconnecting
Controller action marked with @OnConnect() decorator is called once new client connected.
Controller action marked with @OnDisconnect() decorator is called once client disconnected.
Controller action marked with @OnDisconnecting() decorator is called when the client is disconnecting, before the disconnect event.
import { SocketController, OnConnect, OnDisconnect, OnDisconnecting } from 'socket-controllers';
@SocketController()
export class MessageController {
@OnConnect()
save() {
console.log('client connected');
}
@OnDisconnect()
save() {
console.log('client disconnected');
}
@OnDisconnecting()
save() {
console.log('client is disconnecting');
}
}
@ConnectedSocket() decorator
To 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() decorator
To 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 { transformOption: { transform: false ] } in SocketControllerOptions when creating a server.
You can define an index to get multiple parameters from the socket event.
import { SocketController, OnMessage, MessageBody } from 'socket-controllers';
@SocketController()
export class MessageController {
@OnMessage('save')
save(@MessageBody({index: 0}) param1: any, @MessageBody({index: 1}) param2: any) {
console.log('received message: ', message1);
console.log('received message: ', message2);
}
}
@MessageAck() decorator
To get received message ack use @MessageAck() decorator:
import { SocketController, OnMessage, MessageAck, MessageBody } from 'socket-controllers';
@SocketController()
export class MessageController {
@OnMessage('save')
save(@MessageBody() message: any, @MessageAck() ack: Function) {
console.log('received message: ', message);
ack('callback message');
}
}
note: ack must be the last parameter in
emit, otherwise it will benull
@SocketQueryParam() decorator
To 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);
}
}
Get socket client id using @SocketId() decorator
To get connected client id use @SocketId() decorator.
import { SocketController, OnMessage, MessageBody } from 'socket-controllers';
@SocketController()
export class MessageController {
@OnMessage('save')
save(@SocketId() id: string) {}
}
Get access to using socket.io instance using @SocketIO() decorator
import { 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
}
}
Send message back to client after method execution
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_range', {errorType: RangeError})
@EmitOnFail('save_error_type', {errorType: TypeError})
@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.
The order is important when defining multiple @EmitOnFail() decorators, the first matching errorType will be served
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.
Using exist server instead of creating a new one
If you need to create and configure socket.io server manually,
you can pass it to the SocketControllers constructor.
Here is example of creating socket.io server and configuring it with express:
import 'reflect-metadata'; // this shim is required
import { SocketControllers } from 'socket-controllers';
import { Server } from 'socket.io';
import { Container } from 'typedi'; // Only if you are using typedi
const app = require('express')();
const server = require('http').Server(app);
const io = new Server(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();
});
new SocketControllers({io, container: Container});
Load all controllers from the given directory
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 { SocketControllers } from 'socket-controllers';
import { Container } from 'typedi'; // Only if you are using typedi
new SocketControllers({
port: 3000,
container: Container,
controllers: [__dirname + '/controllers/*.js'],
}); // registers all given controllers
Using socket.io namespaces
To listen to messages only of the specific namespace you can mark a controller with namespace:
@SocketController('/messages')
export class MessageController {
// ...
}
Also you can use dynamic namespace, like express router patterns:
@SocketController('/messages/:userId')
export class MessageController {
// ...
}
Using middlewares
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();
}
}
You can limit middlewares to namespaces providing either a string, RegExp or Array<string | RegExp> to the namespace parameter:
import { Middleware, MiddlewareInterface } from 'socket-controllers';
@Middleware({namespace: '/test'})
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();
}
}
Don't forget to load your controllers and middlewares
Controllers and middlewares should be loaded:
import 'reflect-metadata';
import { SocketControllers } from 'socket-controllers';
import { MessageController } from './MessageController';
import { MyMiddleware } from './MyMiddleware'; // here we import it
import { Container } from 'typedi'; // Only if you are using typedi
const server = new SocketControllers({
port: 3000,
container: Container,
controllers: [MessageController],
middlewares: [MyMiddleware],
});
Also you can load them from directories. Also you can use glob patterns:
import 'reflect-metadata';
import { SocketControllers } from 'socket-controllers';
import { Container } from 'typedi'; // Only if you are using typedi
const server = new SocketControllers({
port: 3000,
container: Container,
controllers: [__dirname + '/controllers/**/*.js'],
middlewares: [__dirname + '/middlewares/**/*.js'],
});
Using DI container
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 { SocketControllers } from 'socket-controllers';
import { Container } from 'typedi';
// create and run socket server
const server = new SocketControllers({
port: 3000,
container: Container,
controllers: [__dirname + '/controllers/*.js'],
middlewares: [__dirname + '/middlewares/*.js'],
});
That's it, now you can inject your services into your controllers:
@Service()
@SocketController()
export class MessageController {
constructor(private messageRepository: MessageRepository) {}
// ... controller actions
}
Note: TypeDI won't create instances for unknown classes since 0.9.0, you have to decorate your Class as a
Service()as well.
Scoped controllers
You 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.
You will get a new instance for each event in the controller.
The scopedContainerGetter function receives the SocketEventContext.
The 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.
import 'reflect-metadata';
import { SocketControllers, SocketEventContext } from 'socket-controllers';
import { Container, ContainerInstance, Token } from "typedi";
const myDiToken = new Token();
// create and run socket server
const server = new SocketControllers({
port: 3000,
container: Container,
scopedContainerGetter: (args: SocketEventContext) => {
const container = Container.of(YOUR_REQUEST_CONTEXT);
container.set(myDiToken, 'MY_VALUE');
return container;
},
scopedContainerDisposer: (container: ContainerInstance) => {
container.dispose();
},
controllers: [__dirname + '/controllers/*.js'],
middlewares: [__dirname + '/middlewares/*.js'],
});
Interceptors
Interceptors allow you to wrap your event handlers in higher order functions. With interceptors you can add logging or modify the incoming or outgoing data for event handlers.
import {
SocketController,
OnMessage,
EmitOnSuccess,
EmitOnFail,
SkipEmitOnEmptyResult,
UseInterceptor,
MessageBody
} from 'socket-controllers';
const interceptor: InterceptorInterface = {
use: (ctx: SocketEventContext, next: () => any) => {
ctx.messageArgs[0] = 'modified message from controller - ' + ctx.messageArgs[0];
const resp = next();
return 'modified response from controller - ' + resp; // modified response from controller - modified response from method - reponse
},
};
@Service()
class Interceptor implements InterceptorInterface {
async use(ctx: SocketEventContext, next: () => any) {
ctx.messageArgs[0] = 'modified message from method - ' + ctx.messageArgs[0];
const resp = await next();
return 'modified response from method - ' + resp; // modified response from method - reponse
}
}
@SocketController()
@UseInterceptor(interceptor)
export class MessageController {
@OnMessage('get')
@EmitOnSuccess('get_success')
@SkipEmitOnEmptyResult()
@UseInterceptor(Interceptor)
get(@MessageBody() message: string): Promise<Message[]> {
console.log(message); // modified message from controller - modified message from method - original message
return 'response';
}
}
Interceptors are executed in order of definition, starting with the controller interceptors.
Decorators Reference
| Signature | Description |
|---|---|
| `@SocketController(namespace?: string\ | Regex)` |
@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. |
@OnDisconnecting() | Registers controller's action to be executed when client is disconnecting 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. |
@NspParams() | Injects dynamic namespace params. |
@NspParam(paramName: string) | Injects param from the dynamic namespace. |
@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. |
@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. |
Samples
Take a look on samples in ./sample for more examples of usage.
Related projects
- If you are interested to create controller-based express or koa server use routing-controllers module.
- If you need to use dependency injection in use typedi module.
Repo liên quan
microsoft/vscode is the GitHub repository where Microsoft develops the Visual Studio Code editor together with the community, published under the MIT license with 188,654 stars and 41,615 forks. The README draws a clear line between this open Code - OSS source and the Microsoft-branded Visual Studio Code product, a separate distribution built from this code plus Microsoft-specific customizations.
TypeScript is Microsoft's open-source superset of JavaScript, hosted at microsoft/TypeScript, that layers an optional type system onto JavaScript and compiles it down to standards-based JS. The README describes it as built for application-scale JavaScript across any browser, host, or OS. Most day-to-day compiler bug fixes are now directed to the separate typescript-go repository, while this repo gates changes ahead of TypeScript 7.0.
Deno is an open-source runtime for JavaScript, TypeScript, and WebAssembly, maintained in the denoland/deno GitHub repository under the MIT license. Its own quickstart runs a TypeScript file straight through deno run and requires an explicit --allow-net flag before a program can open a network port. The project also points to JSR, an open-source package registry, and the Deno Standard Library as its dependency and utility resources.
Ant Design is an open-source React UI library and design language maintained at ant-design/ant-design on GitHub. Its README describes pre-built TypeScript components, a CSS-in-JS theme-customization system, and locale support spanning dozens of languages, aimed at enterprise-class web apps. The project has 99,066 stars and 54,690 forks on GitHub and is released under the MIT license.
Trả lời nhanh
typestack/socket-controllers có bao nhiêu sao?
typestack/socket-controllers có 342 sao GitHub — tải lại trang để xem số mới nhất, hoặc xem trực tiếp github.com/typestack/socket-controllers. TopGit phản chiếu số sao của GitHub nhưng không cam kết đến từng phút.
typestack/socket-controllers có những chủ đề gì?
GitHub topics của typestack/socket-controllers: "decorators", "socket", "socket-client", "socket-controllers", "socket-io", "socket-server", "typescript". TopGit xếp repo vào nhóm Frontend.
typestack/socket-controllers còn đang phát triển không?
Commit gần nhất trên typestack/socket-controllers là 8 tháng trước (theo timestamp GitHub). Repo có 49 fork — một chỉ báo về mức độ quan tâm của cộng đồng.
typestack/socket-controllers là gì?
typestack/socket-controllers (typestack/socket-controllers) là dự án TypeScript trên GitHub. Theo mô tả gốc: Use class-based controllers to handle websocket events.
typestack/socket-controllers so với các dự án Frontend khác thế nào?
typestack/socket-controllers được TopGit xếp vào nhóm Frontend, với 342 sao GitHub và viết bằng TypeScript. Xem trang chủ đề Frontend trên TopGit để so sánh với các dự án tương tự theo số sao và mức độ hoạt động.
typestack/socket-controllers viết bằng ngôn ngữ gì?
typestack/socket-controllers chủ yếu viết bằng TypeScript. Trường "language" của GitHub dựa trên phần lớn byte ở nhánh mặc định.
Vì sao typestack/socket-controllers được xếp vào nhóm Frontend?
TopGit xếp typestack/socket-controllers vào nhóm Frontend dựa trên GitHub topics và mô tả của repo (gắn thẻ: "decorators", "socket", "socket-client"). Việc phân loại dựa trên metadata thật của repo, không phải đoán theo cảm tính biên tập.
Đọc đầy đủ README ở tab phía trên.
Muốn nghe thêm một ý kiến về socket-controllers?
Hỏi một AI đọc được trang này — một cú bấm là có ngay nhận định về socket-controllers.