2

I am building my app:

Frontend: ReactJS / Javascript Native Websocket: In my component that uses websocket:

  useEffect(() => {  
    const orgId = localData.get('currentOrganizationId');
    const username = localData.get('username');

    let socket = new WebSocket(process.env.REACT_APP_WEBSOCKET_URL); // this is the AWS WebSocket URL after I have deployed it. 
    // let socket = new WebSocket("ws://127.0.0.1:3001");
    socket.onopen = function(e) {
      console.log('socket on onopen'); 
      const info = JSON.stringify({orgId:orgId, username: username, action: "message"});
      socket.send(info);
    };

    socket.onmessage = function(event) {
      console.log(`[message] Data received from server: ${event.data}`);
    };
    
    socket.onclose = function(event) {
      if (event.wasClean) {
        console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
      } else {
        console.log(`[close] Connection died; code=${event.code}`);
      }
    };
    
    socket.onerror = function(error) {
      console.log(`[error] ${error.message}`);
    };

  }, [])

Backend: NodeJS / ExpressJS / ws websocket library:

./utils/websocket:

import WebSocket from "ws";
import queryString from "query-string";
import { StateManager } from '../state';
import logger from '../logger';


export default async (expressServer) => {

  StateManager.addToState('sockets', {});

  const websocketServer = new WebSocket.Server({
    noServer: true,
    // path: "/websockets",
    path: "/",
  });

  expressServer.on("upgrade", (request, socket, head) => {
    logger.info(`on upgrade: ${JSON.stringify(head)}`);
    websocketServer.handleUpgrade(request, socket, head, (websocket) => {
      websocketServer.emit("connection", websocket, request);
    });
  });

  websocketServer.on("connection", function connection(websocketConnection, connectionRequest) {
      logger.info(`on connection`);

      websocketConnection.on("message", (message) => {
        const info = JSON.parse(message);
        logger.info(`info: ${typeof info}  ${info}`);
        const sockets = StateManager.readFromState('sockets');
        sockets[info.username] = websocketConnection;
      });

  });

  return websocketServer;
};

and in index.ts:

  import websocket from './utils/websocket';

  ...other code

  const app = express();

  ...other code

  const server = app.listen(parseInt(process.env.PORT) || 3001);  

  websocket(server);

While this works well in local dev environment, I am really confused on how to move this to AWS like this:

Frontend WebSocket client ---> AWS API Gateway Websocket API ----> Backend in EC2 instance

I have configured like this: enter image description here

enter image description here

I have read the document, but I am still confused about where to start. How should I configure the $connect, $disconnect, $default routes? How are they related to my current websocket client and nodejs server running in EC2?

How should I change the code to integrate it with AWS API Gateway Websocket API?

Currently there is simply no response: enter image description here

The Websocket URL I provided is indeed correct though.

Even after reading the document, I am still confused about where to start fixing this issue.

1 Answer 1

3

The reason why there is no response when you connect to the websocket is because you do not have the HTTP endpoint setup in your backend express app.

When you connect to the AWS API Gateway WebSocket API, WebSocket API takes action set by your $connect integration. In your current configuration, you have set the VPC Link integration with HTTP Method Any on the target url. Thus, for your backend endpoint to be called, you need to create a HTTP endpoint and set the address of the endpoint to "Endpoint URL."

For example, let's say you have the following route set up.

app.get('/connect', function(req, res) {
    res.send('success');
});

If you set the 'Endpoint URL' to '<BACKEND_URL>/connect', you will receive 'success' when you connect to the WebSocket.

In short, you don't need to be running the websocket server on your backend because WebSocket API takes care of it. Through the route integration, you just need to set up how different methods (which are provided by HTTP endpoints by your backend Node.js server) are invoked based on messages sent via WebSocket.

As a side note, I suggest one of the following options.

  1. If you need to run your own backend, do not use AWS API Gateway WebSocket API because there is no need to. You can use AWS ALB instead.
  2. If you don't have to run your own backend and if the features are not too complex, use AWS Lambda Integration with WebSocket API and take the real-time feature serverless. -- Edit (another suggestion based on the comment) --
  3. If you need to use API Gateway, you can set up the HTTP endpoints on your Node.js backend, and integrate it to the WebSocket API $connect integration. Then, the HTTP endpoint that you integrated will be invoked upon connection.
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the great answer! I think I will go with option 1. However, you mentioned using AWS ALB - I need to run my own Express API backend in an EC2 instance, but I also must put it behind the AWS API Gateway, so that the API Gateway acts as the single point of entry to my backend APIs, not the ALB. I have already configured a Restful API in AWS API Gateway. I am not sure that it can cover websockets as well.
I see. In that case, you can just set up the HTTP endpoints on your Node.js backend, and integrate it to the WebSocket API $connect integration. Then, the HTTP endpoint that you integrated will be invoked upon connection.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.