socketwise
TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published

Socketwise Logo

🔌 A powerful framework for building real-time applications on top of Socket.IO using class-based approach with decorators.

Table of Contents

Installation

  1. Install socketwise:

    Using npm:

    npm install socketwise

    Using yarn:

    yarn add socketwise

    Using pnpm:

    pnpm add socketwise
  2. Install typings for socket.io:

    npm install --save-dev @types/socket.io
  3. Set this options in tsconfig.json file of your project:

    {
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true
    }

Example usage

  1. Create a file called message.portal.ts
import { Socket } from 'socket.io'
import {
  ConnectedSocket,
  Message,
  OnConnect,
  OnDisconnect,
  OnDisconnecting,
  Portal,
  SubscribeMessage
} from 'socketwise'

@Portal()
export class MessagePortal {
  @OnConnect()
  public onConnect(): void {
    console.log('Connected.')
  }

  @OnDisconnecting()
  public onDisconnecting(): void {
    console.log('Disconnecting...')
  }

  @OnDisconnect()
  public onDisconnect(): void {
    console.log('Disconnected.')
  }

  @SubscribeMessage('save')
  public onSave(@ConnectedSocket() socket: Socket, @Message() message: string): void {
    console.log('Received message:', message)
    socket.emit('message_saved')
  }
}
  1. Create a file called index.ts
import { Socketwise } from 'socketwise'
import { MessagePortal } from './message.portal'

new Socketwise({
  port: 3000,
  portals: [MessagePortal]
})
  1. Now you can send save message from your client application using socket.io-client package.

More examples

Loading all portals by suffix

By specifying the suffix of your portal files, you can load all portals from anywhere in your project.

import { Socketwise } from 'socketwise'

new Socketwise({
  port: 3000,
  portals: '*.portal.ts'
})

Loading all middlewares by suffix

By specifying the suffix of your middleware files, you can load all middlewares from anywhere in your project.

import { Socketwise } from 'socketwise'

new Socketwise({
  port: 3000,
  portals: '*.portal.ts',
  middlewares: '*.middleware.ts'
})

Handling connection event

You can handle connection event with @OnConnect() decorator. In this example onConnect method will be called once new client connected.

import { Portal, OnConnect } from 'socketwise'

@Portal()
export class MessagePortal {
  @OnConnect()
  public onConnect(): void {
    console.log('Connected.')
  }
}

Handling disconnecting event

You can handle disconnecting event with @OnDisconnecting() decorator. In this example onDisconnecting method will be called when the client is disconnecting.

import { Portal, OnDisconnecting } from 'socketwise'

@Portal()
export class MessagePortal {
  @OnDisconnecting()
  public onDisconnecting(): void {
    console.log('Disconnecting...')
  }
}

Handling disconnected event

You can handle disconnected event with @OnDisconnect() decorator. In this example onDisconnect method will be called when the client is disconnected.

import { Portal, OnDisconnect } from 'socketwise'

@Portal()
export class MessagePortal {
  @OnDisconnect()
  public onDisconnect(): void {
    console.log('Disconnected.')
  }
}

@ConnectedSocket() decorator

You can get connected socket instance with using @ConnectedSocket() decorator.

import { Socket } from 'socket.io'
import { Portal, SubscribeMessage } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@ConnectedSocket() socket: Socket): void {
    socket.emit('message_saved')
  }
}

@Message() decorator

You can get received message body with using @Message() decorator.

import { Portal, SubscribeMessage, Message } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@Message() message: unknown): void {
    console.log('Received message:', message)
  }
}

@MessageAck() decorator

You can get received message ack with using @MessageAck() decorator.

import { Portal, SubscribeMessage, Message, MessageAck } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@Message() message: unknown, @MessageAck() ack: Function): void {
    console.log('Received message:', message)
    ack('callback value')
  }
}

Note: ack should be the last parameter in the emit function; otherwise, it will be null.

@SocketId() decorator

You can get connected client id with using @SocketId() decorator.

import { Portal, SubscribeMessage, SocketId } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@SocketId() socketId: string): void {
    console.log('Socket id:', socketId)
  }
}

@SocketIO() decorator

You can get connected socket.io instance with using @SocketIO() decorator.

import { Server } from 'socket.io'
import { Portal, SubscribeMessage, SocketIO } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@SocketIO() io: Server): void {}
}

@SocketQueryParam() decorator

You can get received query parameter with using @SocketQueryParam() decorator.

import { Portal, SubscribeMessage, SocketQueryParam } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@SocketQueryParam('token') token: string): void {
    console.log('authorization token from query parameter:', token)
  }
}

@SocketRequest() decorator

You can get request object of socket with using @SocketRequest() decorator.

import { IncomingMessage } from 'http'
import { Portal, SubscribeMessage, SocketRequest } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@SocketRequest() request: IncomingMessage): void {
    console.log('request object:', request)
  }
}

@SocketRooms() decorator

You can get rooms object of socket with using @SocketRooms() decorator.

import { Portal, SubscribeMessage, SocketRooms } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  public onSave(@SocketRooms() rooms: Set<Object>): void {
    console.log('socket rooms:', rooms)
  }
}

@EmitOnSuccess() decorator

You can send message back to client after method execution with using @EmitOnSuccess() decorator.

import { Portal, SubscribeMessage, EmitOnSuccess } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  @EmitOnSuccess('message_saved')
  public onSave(): void {
    // after executing this method, the 'message_saved' message will be sent back to the client
  }
}

If you return something, it will be returned in the emitted message data:

import { Portal, SubscribeMessage, EmitOnSuccess } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  @EmitOnSuccess('message_saved')
  public onSave(): Record<string, unknown> {
    // after executing this method, the 'message_saved' message will be sent back to the client with message object
    return {
      id: 10,
      content: 'message saved successfully'
    }
  }
}

@EmitOnFail() decorator

You have the ability to manage which message will be sent if an error or exception occurs during execution with using @EmitOnFail() decorator.

import { Portal, SubscribeMessage, EmitOnSuccess, EmitOnFail } from 'socketwise'

@Portal()
export class MessagePortal {
  @SubscribeMessage('save')
  @EmitOnSuccess('message_saved')
  @EmitOnFail('custom_save_error', { message: 'custom error message' })
  @EmitOnFail('save_error')
  public onSave(): Record<string, unknown> {
    if (true === true) {
      throw new Error('Error! True is equal to true')
    }
    return {
      id: 10,
      content: 'message saved successfully'
    }
  }
}

After execution of this method save_error message will be sent to the client with Error! True is equal to true error message and custom_save_error message also will be sent to the client with { message: 'custom error message' } message object.

Using socket.io namespaces

Using namespaces

To exclusively listen to messages within a spesific namespace, you can mark a portal with namespace:

import { Portal, SubscribeMessage } from 'socketwise'

@Portal('/users')
export class UserPortal {
  @SubscribeMessage('create')
  public onCreate(): void {}
}

Also you can use dynamic namespace:

import { Portal, SubscribeMessage } from 'socketwise'

@Portal('/users/:userId')
export class UserPortal {
  @SubscribeMessage('create')
  public onCreate(): void {}
}

@NamespaceParams() decorator

You can get dynamic namespace params with using @NamespaceParams() decorator.

import { Portal, SubscribeMessage, NamespaceParams } from 'socketwise'

@Portal('/users/:userId')
export class UserPortal {
  @SubscribeMessage('create')
  public onCreate(@NamespaceParams() params: Record<string, string>): void {
    console.log('Namespace params:', params)
    // for instance, if the userId is 12, the params object will be this: { userId: '12' }
  }
}

@NamespaceParam() decorator

You can get spesific dynamic namespace param with using @NamespaceParam() decorator.

import { Portal, SubscribeMessage, NamespaceParam } from 'socketwise'

@Portal('/users/:userId')
export class UserPortal {
  @SubscribeMessage('create')
  public onCreate(@NamespaceParam('userId') userId: string): void {
    console.log('User id:', userId)
  }
}

Using middlewares

Middlewares refer to functions that are provided to the socketIo.use method. They enable you to establish a set of instructions that will run every time a client connects to the server. You can create your own middlewares using the @Middleware() decorator like below:

import { Socket } from 'socket.io'
import { Middleware, SocketIONextFunc, SocketwiseMiddleware } from 'socketwise'

@Middleware()
export class AuthorizationMiddleware implements SocketwiseMiddleware {
  public use(socket: Socket, next: SocketIONextFunc): void {
    console.log('check authorization...')
    next()
  }
}

You have the option to restrict middlewares to namespaces by specifying a string, RegExp or Array<string | RegExp> for parameter of the @Middleware() decorator.

import { Socket } from 'socket.io'
import { Middleware, SocketIONextFunc, SocketwiseMiddleware } from 'socketwise'

@Middleware('/users')
export class AuthorizationMiddleware implements SocketwiseMiddleware {
  public use(socket: Socket, next: SocketIONextFunc): void {
    console.log('check authorization...')
    next()
  }
}

Creating instance of class from message

If you provide a class type for a parameter decorated with @Message(), socketwise will use the class-transformer to construct an instance of the designated class type using the data received within the message. To deactivate this functionality, you should indicate { useClassTransformer: false } within the SocketwiseOptions when creating a server.

import { Portal, SubscribeMessage, Message } from 'socketwise'

class User {
  private name!: string
  private surname!: string

  public getFullName(): string {
    return this.name + ' ' + this.surname
  }
}

@Portal()
export class UserPortal {
  @SubscribeMessage('save')
  public onSave(@Message() user: User): void {
    console.log('Full name:', user.getFullName())
  }
}

Using dependency injection

Socketwise uses MagnoDI as its built-in DI container and exposes it to users. You can use this container to inject your services into your portals and middlewares.

Let's take a look at how to use it by creating a service:

import { Injectable } from 'socketwise'

@Injectable()
export class LoggerService {
  public log(message: string): void {
    console.log(message)
  }
}

Note: To register our service with the container, we need to mark it with the @Injectable() decorator.

Afterwards, let's inject this service into our portal:

@Portal()
export class MessagePortal {
  constructor(private readonly loggerService: LoggerService) {}

  // portal events...
}

Contributing

  1. Fork this repository.
  2. Create a new branch with feature name.
  3. Create your feature.
  4. Commit and set commit message with feature name.
  5. Push your code to your fork repository.
  6. Create pull request.

License

Socketwise is MIT licensed.

Package Sidebar

Install

npm i socketwise

Weekly Downloads

20

Version

1.0.1

License

MIT

Unpacked Size

201 kB

Total Files

155

Last publish

Collaborators

  • canccevik