There are many good articles introducing AppSync's new Merged API and why it is useful. But, none of them demonstrate how to set one up using Infrastructure-as-Code (IaC). That's what I want to do today. I will help you get past some sticky spots that I encountered with the fledgling documentation along with a couple of pointers for more advanced use cases.
In this article, I follow the app structure laid out in this AWS Online Tech Talk about AppSync Merged APIs. This video, like all the existing tutorials I researched, shows you how to set up a Merged API only via the AWS console. Today, I'll show one way to do so with CloudFormation IaC. Feel free to use the accompanying GitHub repository to review the code and follow along. Together, we'll create three simple APIs: Orders, Users, and the "Merged" API that unifies the other two.
The Users API has one type, User
with two queries and one mutation. The Orders API, similarly, has one type, Order
also with two queries, and one mutation. AppSync will combine our Query
and Mutation
types for us, giving us a unified set of actions in the Merged API. Additionally, it will do the same for our Order
type, where the User service can "own" the logic to serve the User
property of the Order
. This is great for supporting bounded contexts and you can read more about it, including many tools to manage conflicts, in the AWS AppSync Developers Guide.
All the above is covered in great detail in the articles and video I've mentioned. What is not well covered is how to do all this programmatically - via IaC. Let's focus on that now.
IaC
To set up a Merged API over our examples, we need four types of CloudFormation resources defined:
AppSync::GraphQLApi
IAM::Role
AppSync::ApiKey
AppSync::SourceApiAssociation
Let's go through these one at a time and I can explain which parts are straightforward and which parts are not.
AppSync::GraphQLApi
MergedApi:
Type: AWS::AppSync::GraphQLApi
Properties:
ApiType: MERGED
AuthenticationType: API_KEY
MergedApiExecutionRoleArn: !GetAtt MergedApiExecutionRole.Arn
Name: appsync-merged-api
The GraphQLApi
is the main piece to construct. It defines what shows up in your AppSync console list of "APIs". What's new is a property called ApiType
that allows you to select GRAPHQL
(for your Source APIs) or MERGED
(the new API type that we want to create). The other property to pay attention to is MergedApiExecutionRoleArn
which is the IAM role that gives this Merged API permission to "source" from other APIs as well as automatically merge in their schemas. Let's look at that role definition next.
IAM::Role
MergedApiExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- appsync.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: merged-api-source-permissions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- appsync:SourceGraphQL
Resource:
- !Sub arn:aws:appsync:us-east-1:${AWS::AccountId}:apis/${cf:users-source-dev.ApiId}/*
- !Sub arn:aws:appsync:us-east-1:${AWS::AccountId}:apis/${cf:users-source-dev.ApiId}
- !Sub arn:aws:appsync:us-east-1:${AWS::AccountId}:apis/${cf:orders-source-dev.ApiId}/*
- !Sub arn:aws:appsync:us-east-1:${AWS::AccountId}:apis/${cf:orders-source-dev.ApiId}
- PolicyName: merged-api-schema-merge
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- appsync:StartSchemaMerge
Resource:
- !Sub arn:aws:appsync:us-east-1:0123456789:apis/*/sourceApiAssociations/*
RoleName: merged-api-execution-role
This is the most complicated set of IaC we'll encounter in this article, but that is often the case with IAM resources in CloudFormation. There is nothing new for Merged APIs here at the outer structure, only some new actions we are allowed to take. One of those actions is appsync:SourceGraphQL
. This action is necessary for the Merged API to consider another AppSync API as a source. In our case, we make a PolicyDocument
that lets our Merged API "source" from our Orders and Users APIs, via their respective ARNs. One note here is that you need to provide two resource entries per API - one for top-level fields and one, with a /*
suffix, for child resolvers1.
Since I want our Merged API to automatically merge in new changes from its Source APIs, we need to add an additional policy to give it permission to do so. This action is called appsync:StartSchemaMerge
and takes a reference to the ARN(s) of the SourceApiAssociation
we will define next. Note - in my example, I am giving permission to use any sourceApiAssociations
it finds on the AWS account. If you least-privilege-restrict this to a particular SourceApiAssociation
you will end up with circular references in your CloudFormation. I decided to not tackle this Gordian knot right now, and used a couple of splats (*
) to keep moving.
AppSync::SourceApiAssociation
OrdersApiAssociation:
Type: AWS::AppSync::SourceApiAssociation
Properties:
Description: The association of the Orders API to the Merged API
MergedApiIdentifier: !GetAtt MergedApi.ApiId
SourceApiAssociationConfig:
MergeType: AUTO_MERGE
SourceApiIdentifier: ${cf:orders-source-dev.ApiId}
This is the element that binds everything together. With SourceApiAssociation
, we define which APIs are sources for our Merged API. You may have many of these. In our example, I have two. The properties are straightforward with a couple of caveats which I'll explain in a moment. Here, we bind the Merged API to a Source API by providing the ApiId
of each. Additionally, we specify how the merge is to take place with the SourceApiAssociationConfig
property. Our choices are simple: MANUAL_MERGE
or AUTO_MERGE
.
Caveat One
The documentation, as of this writing, states that every single property in SourceApiAssociation
is optional. This is not true. Your CloudFormation will fail with an obtuse error if you omit SourceApiAssociationConfig
. This cost me days of head-scratching until I figured it out.
Caveat Two
I am associating APIs within the same AWS account, so my MergedApiIdentifier
and SourceApiIdentifier
can be just the ApiId
of each. However, if you are merging across AWS accounts, you need to provide the full resource ARN for the API. My example does not use cross-account merging, so the ApiId
will suffice.
AppSync::ApiKey
MergedApiKey:
Type: AWS::AppSync::ApiKey
Properties:
ApiId: !GetAtt MergedApi.ApiId
Description: The merged-api ApiKey
Expires: 1719168324 #2024-06-23 and some change...
This is exactly how AppSync ApiKeys have been defined since the beginning. Nothing is new here, except that your Merged API will need one if your Source APIs use their own.
Advanced Use Cases
This example uses one AuthenticationType
- an API key. However, there are many others you can choose. I'm not going to cover all of them here, but I do want to point out something extra in the case you are using AWS_LAMBDA
for your auth type. In addition to specifying the ARN of the Lambda you use, you must create an AWS::Lambda::Permission
resource that allows the Merged API to invoke your auth Lambda. Without this, you will silently fail with a 401. No log error. Much frustration. You have been warned.
The second bit of weirdness is when your Source API is using AppSync's built-in cache. The merged API will connect to your Source API just fine without a cache of its own, but as you might have guessed, you are no longer caching. To cache, you must--in addition to defining a cache on the Source API--define a cache on the Merged API. Do this with a CloudFormation AWS::AppSync::ApiCache
resource. There is nothing special about its setup to a Merged vs. a Source, but just remember to do it.
Final Thoughts
I am a big fan of the serverless-appsync-plugin
, and make use of it in my sample code for Users and Orders. I fully expect it to, one day, support the creation of AppSync Merged APIs and the direct creation of CloudFormation resources will be unnecessary. Until that time, use what I've discussed here to help your team explore Merged APIs and get comfortable with this helpful new tool. Have fun!
References
AWS Blog - Introducing Merged APIs on AWS AppSync
AWS AppSync Developers Guide - Merged APIs
AWS Online Tech Talks - Enable cross-team, federated GraphQL APIs with AWS AppSync Merged APIs
AWS CloudFormation Documentation - AppSync
GitHub: sls-reference-architectures/appsync-merged
GitHub: serverless-appsync-plugin