# Web Socket

ExpressWebJS offers a robust WebSocket Provider to serve real-time apps.

The server works on pure WebSocket connections (supported by all major browsers) and scales naturally within a cluster of Node.js processes.

# WebSocket Connection

Before Getting started, let's uncomment SocketServiceProvider in Config/app.ts providers section.

    providers: [
    /*
     * ExpressWebJS Framework Service Providers...
     */
    "Elucidate/Route/RouteConfigServiceProvider::class",
    "Elucidate/Database/DatabaseServiceProvider::class",
    /*
     * Application Service Providers...
     */
    "App/Providers/AppServiceProvider::class",
    "App/Providers/RouteServiceProvider::class",
    "App/Providers/SocketServiceProvider::class",  // uncomment this line
  ],

# Basic Example

Let’s build a single room chat server for user messaging.

  ts-node maker make-ws-controller chatController

This will generate our chatController class inside App/Http/Controller/Ws directory. Our chatController class looks like this:

"use strict";
class ChatController {
  protected socket: any;
  constructor(socket: any) {
    this.socket = socket;
    this.setMethodListeners();
  }

  onMessage = (data: any) => {
    // same as: socket.on('message')
  };

  onClose = () => {
    // same as: socket.on('close')
  };

  onError = () => {
    // same as: socket.on('error')
  };

  private setMethodListeners() {
    this.socket.on("message", this.onMessage);
    this.socket.on("close", this.onClose);
    this.socket.on("error", this.onError);
  }
}

export default ChatController;

Next we config WebSocket options in Config/socket.ts file.

"use strict";

/*
|--------------------------------------------------------------------------
| Socket.io Config  
|--------------------------------------------------------------------------
| npm i --save socket.io
*/
export default {
  /*
  |--------------------------------------------------------------------------
  | Path
  |--------------------------------------------------------------------------
  |
  | The base path on which the socket.io server will accept connections.
  | Type {String}
  */
  path: "/expressweb-ws",

  /*
  |--------------------------------------------------------------------------
  | Serve Client
  |--------------------------------------------------------------------------
  |
  | Whether to serve the client files
  | Type {Boolean} 
  */
  serveClient: true,

  /*
  |--------------------------------------------------------------------------
  | Ping Timeout
  |--------------------------------------------------------------------------
  |
  | How many ms without a pong packet to consider the connection closed
  | Type {Number}
  */
  pingTimeout: 5000,

  /*
  |--------------------------------------------------------------------------
  | Ping Interval
  |--------------------------------------------------------------------------
  |
  | How many ms before sending a new ping packet
  |
  */
  pingInterval: 25000,

  /*
  |--------------------------------------------------------------------------
  | Upgrade Timeout
  |--------------------------------------------------------------------------
  |
  | How many ms before an uncompleted transport upgrade is cancelled
  |
  */
  upgradeTimeout: 10000,

  /*
  |--------------------------------------------------------------------------
  | MaxHttpBufferSize	
  |--------------------------------------------------------------------------
  |
  | How many bytes or characters a message can be, before closing the session (to avoid DoS).
  |
  */
  maxHttpBufferSize: 10e7,

  /*
  |--------------------------------------------------------------------------
  | Transports
  |--------------------------------------------------------------------------
  |
  | Transports to allow connections to
  |
  */
  transports: ["polling", "websocket"],

  /*
  |--------------------------------------------------------------------------
  | AllowUpgrades	
  |--------------------------------------------------------------------------
  |
  | Whether to allow transport upgrades
  |
  */
  allowUpgrades: true,

  /*
  |--------------------------------------------------------------------------
  | Socket Cors
  |--------------------------------------------------------------------------
  |
  | Whether to allow transport upgrades
  |
  */
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
    allowedHeaders: ["Origin", "Content-Type", "Content-Length", "Authorization", "Accept", "X-Requested-With"],
    credentials: true,
  },
};

# WebSocket Route.

We will now setup our websocket route pointing to our chatController we already created. Web socket route is located at Routes/socket.ts file.

import Ws from "Elucidate/Socket/WS";
/*
|--------------------------------------------------------------------------
| socket.io
|--------------------------------------------------------------------------
|
| This file is used to register socket.io channels and start the Ws server.
|
*/

export default (socket: any) => {
  Ws.connect(socket);
  // Web socket channel controllers
  Ws.channel("chat", "chatController");
};

Now our chat channel is linked to our chatController in App/Http/Controller/Ws directory.

With everything ready, we can now start emitting messages to our channel by requiring socketBox in our code.

Let say in our product event listener, we want to emit to our websocket, we can do that like so:

"use strict";
import Emitter from "Elucidate/Emitter";
import WS from "App/Service/WebSocket";

class ProductListener{
  /**
   * Handle the event.
   * @param {string} eventName
   * @param {object} data
   */
  constructor(eventName, data) {
    Emitter.bindOnce(eventName, () => {
      WS.channel("chat").emit("message", data);
  }
}

export default ProductListener;

# Event Methods

class ChatController {
  onMessage() {
    // same as: socket.on('message')
  }
  onClose() {
    // same as: socket.on('close')
  }
  onError() {
    // same as: socket.on('error')
  }
}

We can also bind a closure to the Ws.channel method, but having a dedicated controller is the recommended practice.

# Client Code

Let’s switch from server to client and subscribe to the chat channel.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Socket.io test</title>
  </head>
  <body>
    <script src="http://localhost:5000/expressweb-ws/socket.io.js"></script>
    <script>
      const socket = io.connect("http://localhost:5000/", {
        path: "/expressweb-ws",
      });
      socket.to("chat").emit("message", [{ name: "Welcome to my Chat room" }]);
      socket.on("message", (data) => {
        console.log(data);
      });
    </script>
  </body>
</html>