Home > Software engineering >  AWS API Gateway WebSocket API: how to use it with React / NodeJS / native WebSocket?
AWS API Gateway WebSocket API: how to use it with React / NodeJS / native WebSocket?

Time:01-13

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.

CodePudding user response:

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.
  •  Tags:  
  • Related