GraphQL API with AWS AppSync
Before going deep in building and deploying an API using AppSync, I’d like to do some introductions about GraphQL, as well as some AWS AppSync terminologies and architecture.
First, GraphQL is a query language for your API and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and, it is instead, backed by your existing code and data. Originally created by Facebook for their internal use, it became open-source in 2015 and its use has been growing a lot lately.
But what GraphQL differs from HTTP APIs? When we have HTTP microservices for example, we as a developer need to understand the various datastore and services available in our application, i.e. all services would be receiving requests over the public internet over a specific endpoint. (See image below)
Using the GraphQL approach you don’t need to know every service you have in your ecosystem, you just need to ensure your data is mapped in the schema of the GraphQL and use the GraphQL proxy in order to reach those data. The proxy knows each of the services and can retrieve the data accordingly and the developer just needs to know to talk to the GraphQL proxy. As shown in the image below.
When developing an API in GraphQL you can perform 3 kinds of operations:
- Query – The most used operation in order to read data.
- Mutation – Similar to POST, PUT and PATCH in the REST API, used when modifying data in
- Subscription – It’s a standard when you need to push notification to the client, so when a data is changed the notification will be sent to all subscribers. Similar to web-sockets.
Multiple operations can be included in one request, but AppSync limits us to one mutation at a time.
In order to make GraphQL works we need to understand how the schemas are built and designed. Schema is a list of types and operations that define the data you are trying to represent and how data can be retrieved. Below we have an example of a schema. On the right side, we have the schema design, i.e. the entities with their fields and type, and each field could be its own separate datastore or service. And on the left side, we have a sample of a request to the data in GraphQL.
After creating the schema, we can query GraphQL, using a template as shown below. And the greatest thing is that we can add as many properties as we want (as long as it’s mapped in the schema). In the example, if we wanted to retrieve Luke’s planet, we just had to add the “planet” in the query.
Now let’s talk a bit about AppSync, which is an AWS managed service that uses GraphQL to make it easy for mobile and web applications to get exactly the data they need.
When considering a traditional web application architecture, we would have components such as load balancers, application clusters, job services, database, CDN (Content Delivery Network). As shown below:
When we are talking about AppSync architecture, we are dealing only with communication between a web or mobile client and the GraphQL backend, which is powered by DynamoDB, ElasticSearch or AWS Lambdas. A simple design is shown below:
Now that you have an idea on how an AppSync is structured, let’s present some key components of the AppSync. They are resumed in three main ones:
- API – It’s an HTTPS endpoint, where all the components we have in our architecture are behind.
- Data Source – It is any of the AppSync supported providers, such as DynamoDB or ElasticSearch.
- Resolver – It’s responsible for transforming the data from a data source to match the output of a schema. It’s written in Velocity Template Language (VTL) which is used to take queries and transform into database query language.
In this project, we will need following requirements:
- AWS Account in order to deploy our GraphQL services.
Creating a GraphQL Service
In order to build our service we will need to go through 6 steps:
- Create IAM Roles
- Create an API
- Create Data Source
- Choose a Schema
- Connect the API
- Test Drive
Create IAM Roles
In order to create our first GraphQL service we need set some IAM roles, two roles to me more precise:
- Data Source Role – Which will grant access to DynamoDB tables or other data sources.
- Logging Role – Which grants AppSync access to send logs about query resolution, responses, and errors to CloudWatch logs.
Let’s create the logging role first, in order to do that we need access AWS via Console, in the top menu, hit “Services”, go to “Security, Identity & Compliance” section, then “IAM”, once in the IAM landing screen, you should see “Roles” in the left menu.
Hit the “Create Role” button and you should see a screen like below, where you are going to select AppSync, in the AWS service list, and hit “Next: Permissions”.
You are going to notice that “AWSAppSyncPushToCloudWatchLogs” policy will be already pre-populated, as shown below, you just need to click on “Next: Tags’
In the next section you just need to add the tags, it’s an optional step, so just go ahead and click “Next: Review” and you should see a page like below.
Just name your Role (in this case, I named it by GraphQLLoggingRole) and click the “Create Role” button.
For our second role, you just need to go through the entire process again, but name it “GraphQLDataSourceAccessRole” instead. (Note: AWS Console forces any AppSync role to have the CloudWatch policy attached to it). But since we want to keep it simple and apply the least privilege concept here, we can do the following. After creating the data source access role, click on it.
You should see the summary screen as below, you just need to click on the red cross beside the name of the CloudWatch policy, as shown below, and hit “Detach” policy.
After the CloudWatch policy is gone, we need to add the proper data source policy. Click on the “Add inline policy” link, select the JSON tab and add the following JSON to the editor, finally click on the “Review policy” button.
Last, we need to name our policy (in this case, “DynamoDBAccessPolicy”) and hit the “Create policy” button.
That is it, we have our 2 roles created for our service. Next step, we are going to create the API.
Create an API
In order to create our API, we need to go to AppSync in the console, in the top menu, go to “Services”, in the “Mobile” section, click “AWS AppSync”. You should see a page like below.
Let’s start clicking on the “Create API” button, select the “Build from scratch” box, and hit the “Start” button in the “Customize your API or import from Amazon DynamoDB” section.
At last, name your application (in my case, Pets App) and hit the “Create” button.
You should see a screen as below, and the first thing we need to do is to configure those roles we created in the last section of this article.
In the left menu, go to “Settings”, in the “Logging” section, we toggle “Enable Logs”, select “All” as log level and attach the existing role to this API, as shown below. (Don’t forget to save your changes)
In the settings you can check some interesting information, such as, API URL, API ID and others. Before creating our schema we need first, to create our data source. i.e. our DynamoDB table.
Create Data Source
In order to store all data, we need to create a table in DynamoDB, in order to do that, we go to AWS Console, in the top menu, go to “Services”, then “Database” section and click on DynamoDB, you should see something like below.
Click on the “Create Table” button and then name your table as shown below and hit “Create”.
It will take a couple of minutes for the table to be created. But as soon as it is created you should see something like below.
Once the table is created we need to bind this new table with our AppSync API, in order to do that, we need to go to our AppSync console. Then in the left menu we need to go to the Data Sources option and configure it as follows.
And there we have it, our DynamoDB table configured to be used by AppSync.
Choose a Schema
Once we have our API, as well as, our table set properly, it’s time for us to create a schema for our API. We are going to use the structure below.
In order to add it to our API, go to the AppSync console, then “Schema” in the left menu, copy and paste the above structure there.
Simple as that, our schema has been created. Now we need to connect the schema with our data source, and we do this using a Resolver (see definition in the Introduction section of this article).
Connect the API
As mentioned before, in order to connect our API to our date source we make the usage of Resolvers. And resolvers are written in VTL, which is sort of a programming language. The main goal of this article is to show how AppSync works with GraphQL, so I won’t be writing much about VTL and its syntax. But if you want to know more about this, please go to (https://velocity.apache.org/engine/1.7/vtl-reference.html). All code required for this article is going to be here, as well as, in the github.
Each Resolver has one request template and a response template, both written in VTL.
Setting up Mutation operation
In order to set up a resolver to our DynamoDB table, we need to go to our “Schema” page, look for the Mutation field and click in “Attach”. As shown below.
Then select “PetDataSource” as the data source name and the Request and Response templates are going to appear.
Entry the VTL code as below. (The template provided by AWS should be good). But if you see any error use below.
Setting up the Query operation
Back to the “Schema” screen, find the Query operation, find getPet field and click “Attach”, once again select the “PetDataSource” and configure the following Request and Response templates.
Setting up the Query List operation
Back to the “Schema” screen, find the Query operation, find listPets field and click “Attach”, once again select the “PetDataSource” and configure the following Request and Response templates.
After completing all these configurations, you’ll be ready to the next step which is to test your queries and mutation.
In order to test your API, you can use the “Queries” tool, in the left menu inside the AppSync painel, there you can play around with your fields, inside the queries and mutations, as well as, check the results and payloads. Below you can find some queries in order to test it.
In order to make an API call from outside the console and integrate it with an application, you will need to use Cognito in order to exchange tokens and credentials with AWS. But this subject is beyond this article’s boundaries and we can discuss it in further discussions.