1

Appsync is already replacing API Gateway to some extend, then why do you need to expose it via API Gateway. I know most people would be asking this question. Here is why?

  1. Support for Usage Plan
  2. Possibility of monetization.

As far as I understood, Appsync is GrapQL + Apollo server implementation. The API exposed supports POST request. And even the subscription request is also a post request with Websocket MQTT (AWS IoT) URL as the response. (Eg provided below)

{
  "extensions": {
    "subscription": {
      "mqttConnections": [
        {
          "url": "wss://something-ats.iot.ap-northeast-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
          "topics": [
            ".../.../subscribeToVehicleLocation/..."
          ],
          "client": "..."
        }
      ],
      "newSubscriptions": {
        "subscribeToVehicleLocation": {
          "topic": ".../../subscribeToVehicleLocation/..",
          "expireTime": null
        }
      }
    }
  },
  "data": {
    "subscribeToVehicleLocation": null
  }
}

If that is the case, can we expose Appsyn endpoint via API-Gateway (POST Method)?

For simplicity, I tried with HTTP API in API-Gateway.

  • It worked well for Query & Mutate Request.
  • But for Subscribe request, I am getting a handshake exception. (Connection failed: Connection handshake error, in my angular amplify project)

    1. Is this the right way to expose Appsync via API-Gateway? Or should I use AWS Service (In API Gateway) to Invoke Appsync?

    2. How can I resolve this Websocket Connection handshake error in Angular Amplify Project?

PS:

I was previously able to subscribe to the data using the original Appsync URL (using AmplifyJS in Angular 7). With API-Gateway URL, I am getting this WS Handshake exception (with Amplify).

WebSocket connection to 'wss://....execute-api.ap-northeast-1.amazonaws.com/graphql?header=...&payload=e30=' failed: Error during WebSocket handshake: Unexpected response code: 400

in AWSAppSyncRealTimeProvider.js:603 

Update 24-04-2020

I was able to invoke Appsync via AWS Service invoke in API-Gateway with the below settings. (Used REST Protocol provided by API Gateway)

But still, I am having the Web Socket error in Amplify

API-Gateway Configuration

AWS API Gateway invoking AWS Service Appsync

Note: AWS Subdomain, is the subdomain part of Appsync API Endpoint.

Trust relationship for IAM Role

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "appsync.amazonaws.com",
          "apigateway.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

IAM Role Permission

API Gateway execution role

Arjun Sunil Kumar
  • 1,781
  • 3
  • 28
  • 46
  • Do you have appsync in the role of API gateway? – Robert Navado Apr 20 '20 at 13:06
  • Hey @RobertNavado, Updated the question. Please note, I was not using `AWS Service Integrate` for invoking Appsync. But wanted to know, which is the recommended approach for this. – Arjun Sunil Kumar Apr 20 '20 at 14:05
  • I don't understand why you can't use the AppSync provided URL for the subscription still and have to Proxy it through API Gateway. You could basically proxy queries/mutations through API Gateway and use the web socket URL by itself. – Ionut Trestian Apr 21 '20 at 01:34
  • @IonutTrestian Thank you. But this won't serve my Quota requirement – Arjun Sunil Kumar Apr 22 '20 at 11:39

2 Answers2

0

I don't think that you are able to make the connection in the way that you imagined.

In AppSync, queries and mutations are delivered using normal HTTP connections (REST). On the other side, subscriptions are based on websockets. Both protocols are TCP based, but they are treated differently by servers and clients (browsers and sdk's, like amplify).

AWS API Gateway supports WebSockets, but using a different configuration.

You told us that you configured the API Gateway to use HTTP API for simplicity. In that case it will not work because the endpoint that you created inside API Gateway is not prepared to perform the websocket handshake with the client (Amplify).

To accept and control the handshake you must create the API as a Websocket API. But the thing is: using this API you can't just forward your call to AppSync. You will need to deploy a component to implement the connection control as expected by API Gateway WebSocket Implementation.

A Lambda Function would be the first idea, but then, there is the question: How you will control and persist the websocket connection from your Lambda to AppSync API. You can't count on Lambda to do that.

I can imagine a following implementation to work on your case (but I don't think that this will be a good implementation):

  1. Implement a WebSocket API in AWS API Gateway
  2. Deploy a component to control de WebSocket API Backend (this must be inside an instance or a container)
  3. From this component, you use amplify to connect to the websocket endpoint in AppSync.
  4. In the component, each time that you receive a request from AppSync to a specific websocket connection, you call AWS API Gateway callback url for the WebSocket API and forward the response.

In summary, with this solution you'll need to reproduce the connection control that is provided by AppSync out of the box, besides having another part of infrastructure to provide the plumbing that is already provided by AppSync out of the box.

Gustavo Tavares
  • 2,579
  • 15
  • 29
  • Thank you. `But In that case, it will not work because the endpoint that you created inside API Gateway is not prepared to perform the websocket handshake with the client (Amplify).` If you see, the subscribe response is nothing but a callback URL for AWS IoT Websocket. Inside Amplify library it is using the callback URL, and then connecting to the WebSocket using that callback URL. Nothing to do with API-Gateway URL. And even with subscribe (exposed via API-Gateway), I am getting the same response. It is just that some handshake is failing with the IoT Hub URL. – Arjun Sunil Kumar Apr 22 '20 at 11:50
  • I don't know exactly how this is implemented inside API Gateway. But the WebSocket handshake is different for the normal HTTP handshake. You can find more information [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism). – Gustavo Tavares Apr 22 '20 at 13:31
  • Besides, REST and HTTP API's have a [downstream integration timeout of 30 seconds](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism). – Gustavo Tavares Apr 22 '20 at 13:31
  • I imagine that because of these two reasons, websocket connections would not be implemented in REST or HTTP API api's inside AWS API Gateway. Only for WebSocket API's. Certainly I can be wrong about it... But doesn't make too much sense for me to implement the needed handshake if the same product provides an alternative for that case (WebSocket API). – Gustavo Tavares Apr 22 '20 at 13:33
0

After getting expert opinions, I had finally dropped the plan of exposing Appsync via API-Gateway.

Appsync & API-Gateway belongs to the same hierarchy in AWS Stack. It was not a good idea to expose Appsync via API-Gateway, since, Appsync endpoint would still be public (leading to a back door).

Below are my solutions (considering Appsync alone) for

  1. Monetization Scope: Collect Appsync metrics/trace logs and calculate the API Usage based on Cognito UserId or Apikey.

  2. Usage Plan/Quota: Set up a Lambda Datasource (in Pipeline resolver) incrementing the hit count in Redis cache (with key as Apikey and value as hit count having a custom TTL say 1 day).

If there are any better solutions, please feel free to share it.

Arjun Sunil Kumar
  • 1,781
  • 3
  • 28
  • 46