11
votes

I am setting up an AWS API Gateway Websockets with a custom authorizer on the $connect route, as described here:

https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-route-keys-connect-disconnect.html

My question is -- how do I get the connectionID, ie the identifier I can use to later broadcast to that connected client?

8
how you get connectionID in $connect using HTTP ass integration type? I tried the given answer but i am getting error.Can you help how you resolve this??Rishabh Garg

8 Answers

13
votes

To add content to the integration request, you'll need to use a Request Template.

  1. Turn off HTTP Proxy Integration for your route. (You can't modify the request otherwise.)
  2. Save your changes. (the Request Templates section won't appear until you do so.)
  3. Set your Template Selection Expression. This is used to look up a value from the incoming request object. (If you want to match all incoming requests, enter \$default. Note the slash. Full documentation here.)
  4. Set your Template Key. This is compared against the value selected by the Template Selection Expression. If it's a match, the template is used. (If you want to match all incoming requests, enter $default. Note the absence of a slash.)
  5. Click your Template Key to open the template editor. This is where you enter your template that will be sent to your integration endpoint as the body of the request. For example, if you want to forward the connection ID and the incoming query parameters to the integration endpoint, you can use the following:
{
    "myConnectionIdProperty": "$context.connectionId",
    "myQueryParams": $input.params()
}

Documentation for variables available to you in the template expression can be found here.

(Note that while $request is a valid variable in the Template Selection Expression, it is not a valid variable in the template itself. Use $input instead there.)

7
votes

The issue here was when using HTTP as integration type -- ie having http endpoints being hit when one or more of your API GW routes are invoked ($connect/$disconnect/$invoke). We found that we could not proxy the request (meaning we lose original headers, including auth), but if we need the connection id, we have to specify a Request Template and use something like:

{"connectionId": "$context.connectionId", "body": "$input.body"}

7
votes

Thanks everyone on trying to document on behalf of AWS. It's a shame that AWS isn't doing it well.

To be able to send data from AWS Websocket API Gateway to your integrated service using VPC Link do the following:

  1. Uncheck Use Proxy Integration

  2. Save

  3. Set Request Template as in the following image: Request Template Set Up of AWS Websocket API Gateway

  4. This will allow to get connectionId, query, body in the body of request.

  5. Save

  6. Click on Add Integration Response on the top right

  7. Set Integration Response as in the following image: Integration Response Setup

8. Hopefully, it will work. If not, please figure out by testing and then put in the answers here for others. Because AWS would not bother to put up good documentation. Thank you.

I would suggest forget any other type of integration and just use lambda. Because the next problem you will face is getting query parameters that were passed at the time of connection in integration of disconnect.

4
votes

The question does not specify what type of back end is used. The AWS documentation for AWS API Gateway is geared towards lambda functions - I could not find any help to get the connectionId to my http back end.

Trying the answer by Big Endian, I found some issues - my cakephp back end would not decode the quoted json body. I found the solution, but there were many other steps needed to implement his answer, so here they are:

I created a route key with HTTP Proxy off, and setting up a request template as follows (also very sparse documentation):

  • Route Selection Expression: $request.body.action
  • Route key: subscribe
    This means all requests with {"action": "subscribe"} will route through here

  • Integration Request type: HTTP

  • HTTP Proxy Integration: off - to header or auth pass through
  • HTTP Method: POST

And then for the hard part: setting up the request template. I wanted all "subscribe" requests to use this template and the only way I found of doing this is setting the Template Selection Expression to the same as the Route Selection Expression: $request.body.action, and setting the Template Key to "subscribe".

It ends up being a double test for the same content that the API must do to apply this template - and if anybody has a better way of doing this, please comment.

And then the last step is to enter this as the "Generate template" for 'subscribe':

{"connectionId": "$context.connectionId", "body": $input.body}

Note that in my case, my body was json and the $input.body is not in quotes - the body json gets expanded by the template. I suppose if the body is just a string then the template will be

{"connectionId": "$context.connectionId", "body": "$input.body"}

but then the routing will never get here since the routing needs body to contain the action key in json.

4
votes

Many thanks to Francois Stark, extremely helpful.

Through my own experimenting, I found you can avoid the need to match specific values of $request.body.action using the $default route and the following format:

  • Template Selection Expression: \$default <- that slash is important
  • Template Key: \$default <- again the slash
  • Generate Template with
{"connectionId": "$context.connectionId", "body": $input.body}

With this configuration, your HTTP endpoint should get the connectionID as body data in all values of 'action' using a single route.

In addition, for getting connectionIDs in the $connect and $disconnect routes, the format of the Request Template is the same, but since there is no body data in these events, you can omit the body:

  • Template Selection Expression: \$default <- that slash is important
  • Template Key: \$default <- again the slash
  • Generate Template with
{"connectionId": "$context.connectionId"}

With this configuration, your HTTP endpoint should get the connectionID as body data in the $connect and $disconnect events.

3
votes

To resolve this, go to your AWS console -> open your AWS CloudShell (Top right in your dashboard)

For the WebSocket server using HTTP_PROXY, you need to modify the @connect route to add the connectionId

1- Enter in the shell:

# List all integrations

aws apigatewayv2 get-integrations --api-id xxxxxxxxx

# Update all @connect integration

aws apigatewayv2 update-integration --integration-id zzzzzzzz --api-id xxxxxxxxx --request-parameters 'integration.request.header.connectionId'='context.connectionId'

2- Don't forget to deploy after or it won't work

Why AWS don't provide you with the connectionId? Because they want you to use Lambda.

0
votes

You can get connectionId from context variables. See this for the available variables for WebSocket APIs: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-mapping-template-reference.html

In a Lambda function, you can access context variables via event.requestContext.

0
votes

You can get the connectionId from event.requestContext.

exports.handler = async (event) => {
const data = event['body'];
const connectionId = event['requestContext']['connectionId'];};