16

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?

Big Endian
  • 944
  • 1
  • 6
  • 23
  • 1
    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 May 07 '19 at 14:10
  • @RishabhGarg try with `"integration.request.header.connectionId": "$context.connectionId"` – Kid_Learning_C Jan 17 '22 at 15:05

8 Answers8

17

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.)

Ben Grabow
  • 281
  • 3
  • 6
  • I followed your instructions and was able to see the transformed request body in cloudwatch. However, the request body after transformation is missing in integration backend. I've tried many things but am stuck on this last step. Could you please take a look? https://stackoverflow.com/questions/70738217/aws-api-gatewat-websocket-request-templates-body-of-request-after-transformatio – Kid_Learning_C Jan 17 '22 at 13:26
13

Thanks everyone on trying to document AWS API Gateway.

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. 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.

Vinit Khandelwal
  • 490
  • 8
  • 20
  • I followed these steps and was able to see the transformed request body in cloudwatch. However, the request body after transformation is missing in integration backend. I've tried many things but am stuck on this last step. Could you please take a look? https://stackoverflow.com/questions/70738217/aws-api-gatewat-websocket-request-templates-body-of-request-after-transformatio – Kid_Learning_C Jan 17 '22 at 13:26
9

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Fractal Mind
  • 405
  • 4
  • 10
  • 1
    I tried almost everything to get connectionId in $connect, $disconnect and $default routes for HTTP Implementation but was not **Deploying** :P Thanks for reminding to **Deploy**. Everything worked like charm! – Saurav Kumar Sep 06 '21 at 09:25
  • 1
    Oh man! I have tried all the other answers above, and they do not work because I was trying to use my Express backend running in EC2. I did not realize that I should have used Lambda instead. Finally your answer solves my problem right on the spot!! If you can simply paste your answer to https://stackoverflow.com/questions/70738217/aws-api-gatewat-websocket-request-templates-body-of-request-after-transformatio, I'll accept it as the answer. (It may help someone in the future) – Kid_Learning_C Jan 17 '22 at 13:41
  • @Kid_Learning_C: please don't advocate for copy+paste answering here. Duplicate answers are removed on Stack Overflow. (We prefer to close one question as a dup of another). – halfer Jan 17 '22 at 13:42
  • 1
    @halfer Thank you for the reminder. you are right - but It is not quite the duplicate question though. What I can do is to mark my post as a possible dup to this one, and add the link to this answer as well. – Kid_Learning_C Jan 17 '22 at 13:46
  • Perfect - that's ideal. Thanks! (We don't link to answers in questions though - questions should not contain answer material.) – halfer Jan 17 '22 at 13:46
8

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.

Francois Stark
  • 153
  • 2
  • 10
  • Thanks for the answer, which helps me out a lot! Can you explain why you used `$input.body` instead of `$request.body` as mentioned in the document? – quanguyen Oct 03 '19 at 23:51
  • I followed the steps of the accept answer and was able to see the transformed request body in cloudwatch. However, the request body after transformation is missing in integration backend. I've tried many things but am stuck on this last step: https://stackoverflow.com/questions/70738217/aws-api-gatewat-websocket-request-templates-body-of-request-after-transformatio I think your answer is the way to go, having realized that API Gateway's WebSocket API is geared towards Lambda functions instead of other types of backend integrations – Kid_Learning_C Jan 17 '22 at 13:30
7

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"}

Big Endian
  • 944
  • 1
  • 6
  • 23
  • According to this document: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-selection-expressions.html / `Route Selection Expression` section, `$request.body` can be used but I always get "". Can you explain why? – quanguyen Oct 03 '19 at 23:54
  • I followed these steps and was able to see the transformed request body in cloudwatch. However, the request body after transformation is missing in integration backend. I've tried many things but am stuck on this last step. Could you please take a look? https://stackoverflow.com/questions/70738217/aws-api-gatewat-websocket-request-templates-body-of-request-after-transformatio – Kid_Learning_C Jan 17 '22 at 13:27
4

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.

MikeGuz
  • 41
  • 2
  • When I am doing Template Selection Expression using \$default. socket connection is not establishing. – Rishabh Garg May 07 '19 at 10:35
  • I had a similar problem with ended up resolving it by making an integration response and a method response (as described above), which serve no apparent purpose other than allowing connections to work. – MikeGuz May 08 '19 at 15:37
1

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.

Jaewoo Ahn
  • 59
  • 1
-2

You can get the connectionId from event.requestContext.

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