2

I am currently making a backend server that uses Express and Websockets in Typescript. From a high level, my issue is that I would like to make it so that when I receive a POST to one of my routes, the handler for that route sends a message to a client websocket. I currently have three files: app.ts, conversationController.ts, and server.ts.

The way that I've structured it is that conversationController.ts handles the http and websocket logic for my route, app.ts uses that controller and exports my express app, and server.ts is what creates both the HTTP server and Websocket server. Here's my code so you can get a feel for what is going on and what I'm trying to do. As I'm sure you'll see, I am doing things in a bit of a funky way; I would love to get feedback on how I can structure things better/best practices, but either way, my code right now does work. There is a bit more explained at the bottom.

Here's the important stuff from conversationController.ts

import { NextFunction, Response, Request, Router, } from "express";

export class ConversationController {
        public router: Router;

        constructor() {
            this.router = Router();
            this.setupRoutes();
        }

        private handleMessageFromAndroid(req: Request, res: Response, next: NextFunction) {
            const message = {
                phoneNumber: req.body.phoneNumber,
                textMessageBody: req.body.textMessageBody,
            };

            /* here is where I would somehow try to have access to the websocket connection that is made below in socketLogic(), so that I can tell the client of the socket that the backend has received data */           
            /* ideally it would look something like this: */               

            // ws.send("Received a POST call to the server, sending the data upstream to you, the client websocket on the front-end.");

            res.sendStatus(200);
        }

        private setupRoutes(): void {
            this.router.post("/", this.handleMessageFromAndroid.bind(this));
        }

        public socketLogic(wss: Server): void {
            wss.on("connection", (ws) => {
                console.log("Connection made to client websocket.");

                /* once again, I would like to somehow have access to this "ws" object up in handleMessageFromAndroid*/
                ws.on("message", (data: string) => {
                    const message = JSON.parse(data);
                    console.log("Received message from client: ", message);
                    ws.send("Thanks for sending a message to the server.");
                });
            });
        }
}

Here's the important stuff from app.ts

import * as express from "express";
import { ConversationController } from "./controllers/conversation";

export const app = express();

app.use("/texts", new ConversationController().router);

Finally, here's server.ts

import * as http from "http";
import * as WebSocket from "ws";
import { app } from "./app";
import { ConversationController } from "./controllers/conversation";

const server = app.listen(app.get("port"), () => {
    console.log("App is running at http://localhost:%d in %s mode", app.get("port"), app.get("env"));
});

export const wss = new WebSocket.Server({ server });
new ConversationController().socketLogic(wss);

Now, let me just be very clear. This code does work. I can connect to the WebSocket server on my front-end; I receive the message saying that I connected, and I can send messages back and forth through the socket. HOWEVER, my issue is that I would like to somehow make the opened socket connection (in the socketLogic() method; the socket called "ws") available to the handleMessageFromAndroid method in conversationController.ts.

At first, I was thinking to import the WebSocket server (the one created in server.ts) into conversationController.ts and just make it an instance variable so that the server would be available to the entire class and that would have solved all of my problems, but that led me to find out about cylic dependencies. Basically, when I tried that, the WebSocket server from server.ts is undefined since conversationController.ts gets compiled before server.ts.

I'm not sure exactly how to solve this issue, or if it can even be done. But I would definitely appreciate some help as I've been working on solving this issue for a few days now and have checked many SO questions and Googled quite a bit. Thanks.

n0k
  • 23
  • 5
  • To get access to the webSocket that corresponds to a particular incoming request, the usual scheme is to insert the webSocket object into a session object (when it connects) so when you get an incoming http request, you can look in the session to get the corresponding webSocket object and can then use that to send to the right client. At the time of webSocket connection, you can use the cookies that come with that initial request to know which session to put the connection in. If using express and socket.io, there's https://www.npmjs.com/package/express-socket.io-session. – jfriend00 Jan 07 '18 at 20:40
  • Interesting! Thanks I'm definitely gonna take a look at that. – n0k Jan 08 '18 at 17:55

0 Answers0