title: How to programmatically connect to AWS websocket API Gateway
body: <p>I am trying to implement messaging between users on my website by leveraging AWS's websocket api gateway. Every guide/documentation that I look at says to use wscat to test the connection to the gateway. I am at the point where I can connect to the api gateway and send messages between clients using wscat but am struggling to get it working programmatically from my ts code.</p>
  
  <p>What I want to do is make an api call to the websocket api gateway once the user logs in so they can send messages at any point. I am using serverless for my backend and Angular 6 for the front end. I read that I need to make a <code>POST</code> request to <code>https://{api-id}.execute-api.us-east-1.amazonaws.com/{stage}/@connections/{connection_id}</code> to send messages through a websocket connection but i'm having trouble using typescript in a service I created to connect/get a connection id.</p>
  
  <p>I am making a second API call after the user successfully logs in to open a connection to the websocket api gateway. I tried calling a function that makes a post request with no body (not sure what I would send in the body of the request since I've only connected to it using the wscat tool) to the URL I get after deploying my serverless code. I also tried making a POST request to the https:// URL I see in the AWS console after manually deploying the API gateway. </p>
  
  <p>base.service.ts</p>
  
  <pre><code>protected getBaseSocketEndpoint(): string {
          // 'wss://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev' &lt;-- tried this too
          return 'https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/@connections';
      }
  </code></pre>
  
  <p>authentication.service.ts</p>
  
  <pre><code>this.authService.login(username, password).pipe(first()).subscribe(
              (response) =&gt; {
                  console.log(response);
                  this.authService.setCookie('userId', response.idToken.payload.sub);
                  this.authService.setCookie('jwtToken', response.idToken.jwtToken);
                  this.authService.setCookie('userEmail', response.idToken.payload.email);
                  this.authService.setCookie('refreshToken', response.refreshToken.token);
  
                  this.toastr.success('Login successful. Redirecting to your dashboard.', 'Success!', {
                      timeOut: 1500
                  });
  
                  this.authService.connectToWebSocket(response.idToken.payload.sub).pipe(first()).subscribe(
                      response =&gt; {
                          console.log(response);
                      }
                  );
  
                  this.routerService.routeToUserDashboard();
              },
              (error) =&gt; {
                  // const errorMessage = JSON.parse(error._body).message;
                  this.toastr.error("Incorrect username and password combination.", 'Error!', {
                      timeOut: 1500
                  });
              }
          );
  </code></pre>
  
  <p>authentication.service.ts extends BaseService</p>
  
  <pre><code>public connectToWebSocket(userId: string): Observable&lt;any&gt; {
          const body = {
              userId: userId
          };
  
          console.log('calling connectToWebSocket()..');
          return this.http.post(this.getBaseSocketEndpoint(), body).pipe(map(response =&gt; {
              console.log(response);
          }));
      }
  </code></pre>
  
  <p>serverless.yaml</p>
  
  <pre><code>functions:
    connectionHandler:
      handler: connectionHandler.connectionHandler
      events:
        - websocket:
            route: $connect
            cors: true
        - websocket:
            route: $disconnect
            cors: true
    defaultHandler:
      handler: connectionHandler.defaultHandler
      events:
        - websocket:
            route: $default
            cors: true
    sendMessageHandler:
      handler: messageHandler.sendMessageHandler
      events:
        - websocket:
            route: sendMessage
            cors: true
  </code></pre>
  
  <p>connectionHandler.js (lambda)</p>
  
  <pre><code>const success = {
    statusCode: 200,
    headers: { "Access-Control-Allow-Origin": "*" },
    body: "everything is alright"
  };
  
  module.exports.connectionHandler = (event, context, callback) =&gt; {
    var connectionId = event.requestContext.connectionId;
    if (event.requestContext.eventType === "CONNECT") {
      addConnection(
        connectionId,
        "b72656eb-db8e-4f32-a6b5-bde4943109ef",
        callback
      )
        .then(() =&gt; {
          console.log("Connected!");
          callback(null, success);
        })
        .catch(err =&gt; {
          callback(null, JSON.stringify(err));
        });
    } else if (event.requestContext.eventType === "DISCONNECT") {
      deleteConnection(
        connectionId,
        "b72656eb-db8e-4f32-a6b5-bde4943109ef",
        callback
      )
        .then(() =&gt; {
          console.log("Disconnected!");
          callback(null, success);
        })
        .catch(err =&gt; {
          callback(null, {
            statusCode: 500,
            body: "Failed to connect: " + JSON.stringify(err)
          });
        });
    }
  };
  
  // THIS ONE DOESNT DO ANYHTING
  module.exports.defaultHandler = (event, context, callback) =&gt; {
    callback(null, {
      statusCode: 200,
      body: "default handler was called."
    });
  };
  
  const addConnection = (connectionId, userId, callback) =&gt; {
    const params = {
      TableName: CHATCONNECTION_TABLE,
      Item: {
        connectionId: connectionId,
        userId: userId
      }
    };
  
    var response;
    return dynamo
      .put(params, function(err, data) {
        if (err) {
          errorHandler.respond(err, callback);
          return;
        } else {
          response = {
            statusCode: 200,
            headers: { "Access-Control-Allow-Origin": "*" },
            body: JSON.stringify(data)
          };
          callback(null, response);
        }
      })
      .promise();
  };
  
  const deleteConnection = (connectionId, userId, callback) =&gt; {
    const params = {
      TableName: CHATCONNECTION_TABLE,
      Key: {
        connectionId: connectionId,
        userId: userId
      }
    };
  
    var response;
    return dynamo
      .delete(params, function(err, data) {
        if (err) {
          errorHandler.respond(err, callback);
          return;
        } else {
          response = {
            statusCode: 200,
            headers: { "Access-Control-Allow-Origin": "*" },
            body: JSON.stringify(data)
          };
          callback(null, response);
        }
      })
      .promise();
  };
  </code></pre>
  
  <p>Expected: trigger POST api call and open a persistent connection with the Websocket API Gateway.</p>
  
  <p>Actual: unable to connect via API call above. I get a 403 in the console with the message:</p>
  
  <p>Access to XMLHttpRequest at '<a href="https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/@connections" rel="noreferrer">https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/@connections</a>' from origin '<a href="http://localhost:4200" rel="noreferrer">http://localhost:4200</a>' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.</p>
  
  <p>Not sure why im getting a CORS error when I have CORS enabled in my serverless file.</p>