Return the connection ID when connecting to AWS API Gateway websocket

Viewed 880

I am using AWS Cloud Formation and struggling to understand how API Gateway works for Websocket.

I'm looking to get the $connect route responding with the OK and the connection ID assigned by AWS API Gateway. So for example when doing

wscat -c wss://my-custom-api-gateway-ws.com

I would expect to get

Connected (press CTRL+C to quit)

and a message such as

< {"message": "Connected". "connectionId": "foo"}

or similar. But I keep getting a

wscat -c wss://my-custom-api-gateway-ws.com
error: Unexpected server response: 500

This is what I've tried

  wsApiGateway:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: !Sub ${prefix}-ws-gateway
      Description: Api Gateway for Websockets
      ProtocolType: WEBSOCKET
      RouteSelectionExpression: $request.body.action
      DisableExecuteApiEndpoint: true

  wsConnectRoute:
    Type: AWS::ApiGatewayV2::Route
    DependsOn:
      - wsConnectIntegration
    Properties:
      ApiId: !Ref wsApiGateway
      RouteKey: $connect
      AuthorizationType: NONE
      OperationName: ConnectRoute
      RouteResponseSelectionExpression: $default
      Target: !Join
        - /
        - - integrations
          - !Ref wsConnectIntegration

  wsConnectIntegration:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref wsApiGateway
      Description: Websocket $connect integration
      IntegrationType: MOCK
      RequestTemplates:
        application/json: "{\"statusCode\": 200, \"connectionId\": \"$context.connectionId\", \"message\": \"Success\"}"
      PayloadFormatVersion: 1.0

  wsApiStage:
    Type: AWS::ApiGatewayV2::Stage
    DependsOn:
      - wsConnectRoute
    Properties:
      StageName: production
      Description: Autodeploy in production
      AutoDeploy: true
      ApiId: !Ref wsApiGateway
      AccessLogSettings:
        DestinationArn: !GetAtt wsApiGatewayLogGroup.Arn
        Format: '{"requestTime":"$context.requestTime","requestId":"$context.requestId","connectionId":"$context.connectionId","domainName":"$context.domainName","stage":"$context.stage","httpMethod":"$context.httpMethod","path":"$context.path","routeKey":"$context.routeKey","status":$context.status,"responseLatency":$context.responseLatency, "responseLength":$context.responseLength, "integrationError":$context.integration.error}'

If I remove the route $connect completely, I can connect but it does not return any message

wscat -c wss://my-custom-api-gateway-ws.com
Connected (press CTRL+C to quit)

And if I send anything through the websocket I am getting an automated response error that contains the connection Id (which I could use as workaround). But I want to get that connection Id when connecting, not when forcing an error.

wscat -c wss://my-custom-api-gateway-ws.com
Connected (press CTRL+C to quit)
> this is a silly message
< {"message": "Forbidden", "connectionId":"H-SaJeT8oECIUg=", "requestId":"H-SbUEHSoEF3JQ="}

My understanding is that I should be able to have it working with MOCK integration type, since I don't need an integration http service or similar and the API Gateway should have all what's needed to respond.

Has anyone achieved something similar?


UPDATE 1 I've manually (no cloud formation yet) followed steps on Setup a basic WebSocket mock in AWS ApiGateway to add a $connect route, with MOCK integration, a response template of

{"statusCode" : 200, "connectionId" : "$context.connectionId"}

but I can't see that connectionId when connecting.

wscat -c wss://foo.execute-api.eu-west-1.amazonaws.com/production
Connected (press CTRL+C to quit)

If, additionally, I add a $default route, then once connected I can send messages to websocket and receive the expected connectionId, but I don't seem to find a way to receive the connectionId only with a $connect route without any additional routes.

1 Answers

You're trying to return an HTTP response to a websocket; once converted to websocket, you can't respond with HTTP anymore.

I just return a null. To send data to the client, launch a Lambda and push data out through the connectionId with the postToConnection API function. (Here's the link for the Javascript SDK.)


Edit:

Hmm. I see in newer code I do return return { statusCode: 200 } from my Lambdas, but that is all. Content still goes out via the API call.

Related