Front-End Web & Mobile

Introducing Private APIs on AWS AppSync

AWS AppSync is a fully managed service that enables developers to create GraphQL APIs that can securely access, manipulate and combine data from one or more data sources. When you create a GraphQL API on AppSync, a public endpoint will be generated which can be used to send queries, mutations and subscriptions requests to the API. However, clients within a private network, like an Amazon Virtual Private Cloud (VPC) or on-premises network, will need a network route through the internet to reach the AppSync public endpoint. This can be a challenge for customers that need to change their security policy and firewall rules to send requests to the AppSync API.

Today, we are announcing the general availability of AppSync Private APIs. With Private APIs, you can restrict access to your GraphQL APIs to clients within a private network, such as a VPC or on-premises data center. Requests to your Private API will go through AWS’s private network without going over the internet. To get started, you can set your API as private when creating your GraphQL API on AppSync and attach an interface VPC endpoint in each of the VPCs in the same AWS account from where you will be invoking the API. You can also use AWS Direct Connect or AWS Site to Site VPN to establish a private connection from your on-premises network to the VPC hosting the interface endpoint. With this connection, clients in your on-premises data center can invoke the Private API.

The diagram below shows how you can connect to your Private API on AWS AppSync from an application running in a VPC. In this example, I have used Amazon Elastic Compute Cloud (EC2) as the compute platform from where the API will be invoked; the architecture will have a similar configuration if other compute platforms like AWS Lambda or AWS Fargate are used to invoke the API. GraphQL requests from your application will be routed via the interface endpoint to AWS AppSync. The interface endpoint uses AWS PrivateLink, a highly available, scalable technology that enables you to privately connect your VPC to services like AWS AppSync (and other AWS and third party services) as if they were in your VPC. It can be deployed in any of the subnets in your VPC and it is recommended to deploy at least one endpoint in each availability zone (AZ) to ensure high availability.

High level architecture for connecting to AppSync Private API from Amazon VPC

Figure 1: High level architecture for connecting to AppSync Private API from Amazon VPC

Creating and connecting to your AppSync Private API

In this section, we will create a Private API on AWS AppSync and then invoke the API from an EC2 instance running in a VPC. We will be doing the following:

  • create a simple Todo API with the API set as private
  • create a VPC spread across two availability zones (AZs) with public and private subnets in each AZ
  • attach an interface endpoint for AWS AppSync to the VPC private subnets
  • invoke the API from EC2 instances deployed in the private subnets

Creating an AppSync Private API

  • Navigate to the AppSync console and click on Create API
  • In Step 1 of the API creation wizard select Design from scratch and click Next. Note: You can also create a Private API while creating a new API from an existing DynamoDB table or creating a real-time API.

Step 1 - Select API Type

  • In Step 2, provide a name for the API and select the checkbox Use private API features in the Private API section and click Next. Selecting this checkbox will make your API private.

Step 2 - Specify API metadata

  • In Step 3 under Create a GraphQL type, select Create type backed by DynamoDB table now and under Model information, provide a name for the GraphQL Type (Model name) and the specify the fields and corresponding data type for each field. Then, provide a name for the DynamoDB table, primary key and sort key and once completed, click Next.

Step 3 - Specify GraphQL Resources

  • In Step 4, review the API details and then click on Create API. It starts creation of the API and DynamoDB table and once completed, navigate to the API settings page to see a new status Private API On, indicating the API is private. You will also see a padlock sign attached to Private APIs on the list of AppSync APIs you have created in the region.

AppSync Setting Page showing Private API On

Create a VPC with public and private subnet

You can create a VPC using the VPC wizard on the VPC console. You can configure the VPC settings to create a VPC with two public and private subnets, route tables, internet gateway and NAT gateway as shown below. The preview sections shows the resources that will be created. Ensure the option Enable DNS resolution and Enabled DNS host name is selected (refer to the VPC documentation for more details about these two values).

VPC layout

Create an Interface Endpoint for AWS AppSync

  • Navigate to the Amazon VPC console and in the left navigation pane, choose Endpoints and then select Create endpoint.
  • You can specify a name tag for the endpoint e.g. appsync-private-api-endpoint
  • For Service category, keep it set to AWS Services.

Create VPC endpoint

  • For Service Name, set it to com.amazonaws.{region}.appsync-api while replacing region with the current region you are working on.
  • For VPC, select the VPC you created in the previous step.
  • Expand the Additional settings section and ensure the checkbox Enable DNS name is selected. This will associate a private hosted zone with the VPC that contains a record set that enables you to leverage Amazon’s private network connectivity to the AppSync service while making requests using the AppSync API url ( e.g. https://{api_url_identifier}.appsync-api.{region}.amazonaws.com/graphql).  Note: Enabling Private DNS will prevent resources in the VPC from being able to invoke other AppSync public APIs using the AppSync generated API URL. This is because the request to the public API will be routed via the interface endpoint which is not allowed for public APIs. To invoke public APIs in this scenario, it is recommended to configure custom domain names on public APIs, which can then be used by resources in the VPC to invoke the public API.

Create VPC Endpoint - select service name

  • Select the two AZs and one subnet in each AZ to deploy the endpoint. I elected to create the endpoints in the private subnets
  • For Security groups, I created a new security group appsync-private-api-endpoint-sg which only allows internal VPC traffic on port 443 (HTTPS). You can configure the security group to narrow it down to the subnet IP address range or the IP address of the resource that will invoke the API.
  • For the VPC endpoint policy, I selected Full access to allow any AWS principle to invoke the private API. If you are using Identity and Access Management (IAM) authorization mode for your AppSync API, it is recommended that you scope down the custom policy to grant the necessary permissions only to the required IAM users and roles.
  • Leave other options as default and select Create endpoint.

select subnets, security group and endpoint policy

Invoking the Private API from the EC2 instances

We will use an EC2 instance deployed in my private subnet to invoke the API. The security group attached to the EC2 instance and VPC endpoint will need to allow inbound and outbound traffic in both directions. You can use AWS System Manager Session Manager to connect to the EC2 instance and VPC Reachability Analyzer to troubleshoot connectivity issues.

Now to invoke the Private API, you can use two different domain name system (DNS) names:

  • Private DNS (if you have enabled private DNS naming while creating the VPC endpoint)
  • Interface VPC endpoint public DNS hostnames

The details page of the VPC endpoints shows these two DNS names as shown below.

VPC details page

With Private DNS, the base URL to invoke the API will be in the format

https://{api_url_identifier}.appsync-api.{region}.amazonaws.com/graphql

which is the same API URL generated by AppSync when the private API was created (refer to the AppSync API settings page on AppSync console). Using private DNS implies you do not need to make any changes to your application if it is already using the AppSync generated API URL.

With the interface VPC endpoint public DNS hostnames, the base URL to invoke the API will be in the format

https://{vpc_endpoint_id}-{endpoint_dns_identifier}.appsync-api.{region}.vpce.amazonaws.com/graphql

or you can also use the AZ specific DNS hostname if you have deployed an endpoint in the AZ,

https://{vpc_endpoint_id}-{endpoint_dns_identifier}-{az_id}.appsync-api.{region}.vpce.amazonaws.com/graphql.

Using the VPC endpoint public host name will require the AppSync API endpoint hostname to be passed as Host or x-appsync-domain header to the request (see example below).

$ curl -v https://{vpc_endpoint_id}-{endpoint_dns_identifier}.appsync-api.{region}.vpce.amazonaws.com/graphql \
-H "Content-Type:application/graphql" \
-H "x-api-key:da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}" \
-H "Host:{api_url_identifier}.appsync-api.{region}.amazonaws.com" \
-d '{"query": "mutation createTodo($createtodoinput: CreateTodoInput!) {createTodo(input: $createtodoinput) {id title description priority}}","variables":"{\"createtodoinput\": {\"title\": \"Hello, world!\",\"description\": \"Hello, world!\",\"priority\": 4}}"}'

To test out the sample Todo API, we will be using the Private DNS to invoke the API. You can use any command line tool of your choice, here I will use curl to send queries and mutations, and wscat to set up subscription. On Amazon EC2 linux, you will need to install npm which is required to install wscat. Replace all values in the curly bracket with the corresponding values from your AWS account.

Testing Mutation Operation – createTodo Request

$ curl -v https://{api_url_identifier}.appsync-api.{region}.amazonaws.com/graphql \
-H "Content-Type:application/graphql" \
-H "x-api-key:da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}" \
-d '{"query": "mutation createTodo($createtodoinput: CreateTodoInput!) {createTodo(input: $createtodoinput) {id title description priority}}","variables":"{\"createtodoinput\": {\"title\": \"Read emails\",\"description\": \"Read emails\",\"priority\": 4}}"}'

Testing Mutation Operation – createTodo Response

{
  "data": {
    "createTodo": {
      "id": "fec31cdd-9a45-414f-85eb-fec711ba0a79",
      "title": "Read emails",
      "description": "Read emails",
      "priority": 4
    }
  }
}

Testing Query Operation – listTodos Request

$ curl -v https://{api_url_identifier}.appsync-api.{region}.amazonaws.com/graphql \
-H "Content-Type:application/graphql" \
-H "x-api-key:da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}" \
-d '{"query": "query listTodos {listTodos {items {id title description priority}}}","variables":"{}"}'

Testing Query Operation – listTodos Response

{
  "data": {
    "listTodos": {
      "items": [
        {
          "description": "Read emails",
          "priority": 4,
          "id": "fec31cdd-9a45-414f-85eb-fec711ba0a79",
          "title": "Read emails"
        }
      ]
    }
  }
}

Testing Subscription Operation – Subscribing to createTodo mutation

For details on setting up GraphQL subscriptions on AppSync, see Building a Real-time WebSocket Client. I will use the EC2 instance in the private subnet in AZ1 as the client initiating the subscription request to onCreateTodo subscription and the EC2 instance in the private subnet in AZ2 as the client invoking the createTodo mutation.

I will run the command below on the EC2 instance in the Private Subnet in AZ1

$ header=`echo '{"host":"{api_url_identifier}.appsync-api.{region}.amazonaws.com","x-api-key":"da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}"}' | base64 -w0`
$ wscat -p 13 -s graphql-ws -c  "wss://{api_url_identifier}.appsync-realtime-api.{region}.amazonaws.com/graphql?header=$header&payload=e30="
Connected (press CTRL+C to quit)
> {"type": "connection_init"}
< {"type":"connection_ack","payload":{"connectionTimeoutMs":300000}}
< {"type":"ka"} 
> {"id":"f7a49717","payload":{"data":"{\"query\":\"subscription onCreateTodo {onCreateTodo {description id priority title}}\",\"variables\":{}}","extensions":{"authorization":{"x-api-key":"da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}","host":"{api_url_identifier}.appsync-api.{region}.amazonaws.com"}}},"type":"start"}
< {"id":"f7a49717","type":"start_ack"}

On the EC2 instance in the Private Subnet in AZ2, I will execute the createTodo Mutation.

$ curl -v https://{api_url_identifier}.appsync-api.{region}.amazonaws.com/graphql \
-H "Content-Type:application/graphql" \
-H "x-api-key:da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}" \
-d '{"query": "mutation createTodo($createtodoinput: CreateTodoInput!) {createTodo(input: $createtodoinput) {id title description priority}}","variables":"{\"createtodoinput\": {\"title\": \"Go to the shops\",\"description\": \"Go to the shops\",\"priority\": 5}}"}'

On the EC2 instance in the Private Subnet in AZ1 I can see that the subscription notification is trigged, and the message notification appears as shown below

< {"id":"f7a49717","type":"data","payload":{"data":{"onCreateTodo":{"description":"Go to the shops","id":"169ce516-b7e8-4a6a-88c1-ab840184359f","priority":5,"title":"Go to the shops"}}}}

If you intend to use the VPC endpoint public host name to setup subscription, here is a sample query to initiate the connection on EC2

$ header=`echo '{"host":"{api_url_identifier}.appsync-api.{region}.amazonaws.com","x-api-key":"da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}"}' | base64 -w0`
$ wscat -H "x-appsync-domain:{api_url_identifier}.appsync-realtime-api.{region}.amazonaws.com" -p 13 -s graphql-ws -c "wss://vpce-0dc6000550d45e160-m78pnq69.appsync-api.{region}.vpce.amazonaws.com/graphql?header=$header&payload=e30="

then a sample query to start the subscription after initializing the connection is as follows:

> {"id":"f7a49717","payload":{"data":"{\"query\":\"subscription onCreateTodo {onCreateTodo {description id priority title}}\",\"variables\":{}}","extensions":{"authorization":{"x-api-key":"da2-{xxxxxxxxxxxxxxxxxxxxxxxxxx}","host":"{api_url_identifier}.appsync-api.{region}.amazonaws.com"}}},"type":"start"}

Important things to know

  • APIs can only be set up as private a creation time. An API cannot be made private after it has been created and a Private API cannot be changed after it has been created.
  • Setting up VPC interface endpoint for AWS AppSync with Private DNS enabled will prevent resources in the VPC from being able to invoke other AppSync public APIs using the AppSync generated API URL. This is because the request to the public API will be routed via the interface endpoint which is not allowed for public APIs. To invoke public APIs in this scenario, it is recommended to configure custom domain names on public APIs, which can then be used by resources in the VPC to invoke the public API.
  • With a VPC interface endpoint for AWS AppSync, you can access any Private API in the same AWS account and region. To further restrict access to a Private API you can consider the following:
    • ensure only the required admins can create VPC endpoint interface for AWS AppSync
    • use a VPC endpoint custom policy to restrict which APIs can be invoked from resources in the VPC
    • for resources in the VPC, it is recommended to use IAM authorization to invoke AppSync APIs; remember to ensure the resources are given scoped down roles to the APIs.
  • To test your private API, the testing client or query editor will need to send requests to the API through the VPC interface endpoint for AWS AppSync. You can setup your testing client either in your VPC or on-premises data center with a network route to the interface endpoint.

Get started today!

AppSync Private APIs are generally available in all AWS regions where AWS AppSync is available today. You can refer to AWS Regional Services List to find out the regions where AppSync is available. To learn more about Private APIs, refer to the AppSync documentation.

About the author

Ozioma Uzoegwu

Ozioma is a Senior Solutions Architect at Amazon Web Services. In his role, he helps customers of all sizes to transform and modernise on AWS cloud platform by providing architectural guidance and best practices. Ozioma has many years of experience with web development, architecture, cloud and IT management. Prior to joining AWS, Ozioma worked with an AWS Advanced Consulting Partner as the Lead Architect for the AWS Practice. He is passionate about software development with a keen interest in building modern applications using serverless technologies.