How to Create a GraphQL API using AWS AppSync | by Janitha Tennakoon | Towards Data Science

Nowadays whenever we talk or think about creating/designing an API what pops to the mind at first is REST. REST(REpresentational State Transfer) has been the go-to standard until recently when developing an API platform. Even though REST became the standard, it did have its own disadvantages. One of the main disadvantages is the inflexibility for the clients who are going to consume them. So even though at the beginning we create our REST API according to client requirements, that API will have very few options when there is a rapid change of requirements in the client. To support these rapid changes clients would have the need to send multiple calls and get multiple unnecessary data along with it.
GraphQL was developed mainly focusing on providing this flexibility for the clients. It was started as an internal project inside Facebook but later they made it open source. The main concept is to let the client have the ability to choose what data to be queried and what data needs to be returned without making multiple API calls. Yes as you read with GraphQL, there are no multiple endpoints, but rather only a single endpoint. To illustrate how GraphQL works let’s try to implement a simple GraphQL API using node.js and express framework. The data model for our GraphQL will be a User.
Following npm packages will be used in our code.
express, express-graphql, graphql
A GraphQL API mainly consists of four components.
Schema
Queries
Mutations
Resolvers
GraphQL schema is the core element where we define the functionalities clients can execute after they are connected to the API. The main building block on schema is type.
Above as you can see using building block type we have created three blocks. type Query, type Mutation, and type User. Query and Mutation will be described below. For type User as you can see we have defined the attribute fields that will be available for clients when querying for User.
Query type is used to define what type of queries will be available for the clients to be accessed. In REST terms Query type can be mapped to GET requests. In the above schema, we have defined three queries with arguments they accept and what the return value type will be. (! stands for required)
Whatever functions which make data change should be done as a Mutation in GraphQL. POST, PUT, and DELETE requests on REST can be mapped as mutations on GraphQL. As we defined queries, mutations are also defined with arguments and return value type.
In resolvers, we define the functionality of queries and mutations we defined in the schema. Resolvers map the schema defined methods to our executing functioning methods.
In above as you can see, for each query and mutation we defined, we have mapped a Javascript function that will execute the functional logic. (getUser, getUserByName, getUserByStatus and updateUser are Javascript functions)
All right, now that we have covered the main concepts in our code, below is the complete code for our simple GraphQL API. As mentioned above you need to install the mentioned npm packages in order to run the application.
Now you can start the node.js server and to issue queries to our API we can use GraphiQL tool which will run on http://localhost:4000/graphql
Now, as shown above, we can execute queries for our created API. Above we are getting the user with an id of 1, and we request only name and age fields to be returned.
Same as queries, we can send mutations to update users as well. Above we have changed the age of the user to 25.
AWS AppSync is a service provided by Amazon Web Services which simplifies the API application development by letting developers create a secure, flexible GraphQL API on their infrastructure. The benefit of using AWS AppSync is that it also provides additional features like Cognito, IAM permissions, API key, and many other AWS services to integrate with our API.
With AppSync main concepts of GraphQL remain mostly the same with one additional type, Subscriptions. Subscriptions are invoked to a mutation done through the API so it can be used to create real-time GrapgQL APIs. Also, we need to talk about two more additional components in AWS AppSync before we start creating our own GraphQL in AWS AppSync.
Data Source — Data source can either be persistent storage (relational database or NoSQL database) or a trigger (AWS Lambda functions or another HTTP API)
Resolver — Have the same concept as a resolver in GrapQL, but here we map request payload to our data sources or triggers. These resolvers mainly compromise with mapping templates which contains execution logics.
Let’s start implementing our API. First go to the AppSync service where you will greeted with the below screen if you have no APIs created already.
Click on Create API which will take us to the API creation page. Here AWS will provide us with several options. We can either choose from one of the already created templates or start from scratch. For this article let’s choose Build from scratch so we will be able to learn how everything connects behind the scene.
Next, provide a Name and create our API. Then we will be forwarded to a screen where we will have the option of editing our schema and running queries against our API.
Since we have nothing on our API yet, let’s first define our schema. In this article, I am going to discuss on two types of data sources that AppSync supports. One is DynamoDB and the other will be AWS Lambda functions. As the above created simple GraphQL API, let’s assume our User data model.
Let’s say we need our Users to be saved in a DynamoDB table. So all the queries and mutations done on type User will be directly happening on our DynamoDB table. So first let’s define our User type in the schema.
On the schema page click on Create Resource where we will define our User schema.
We define the new type of User and then it will ask for the DynamoDB table details. Here we can provide the name of the table and also configure different types of indexes we needed to create as well. And lastly it will show the schema blocks that it has automatically generated for our type User which will be merged to our schema. Click on Create and it will create the DynamoDB table along with resolvers as well.
Now let’s look more into our schema. We can see that AppSync has automatically generated query types and mutations for us and it have already mapped these queries and mutations to resolvers as well.
Let’s look at one resolver to identify how resolvers work on AppSync.
Click on createUser mutation. Here we can see the template that AppSync has used for this mutation. In the request mapping template, we can see that it will take id as the key for our userTable and create a user collection inside the table. $ctx.args.input is the arguments we will pass to our mutation.
Response mapping defines the response we will send back to the client. Here it will directly send the output sent from DynamoDb which will be the newly created user.
We can test our API using the provided Queries tool. Let’s first add a user and try to query the user using the id.
The API so far we created can do all CRUD operations directly on our DynamoDB database. But APIs do not only contain CRUD operations. There can be different kinds of functional logics like starting a process by sending messages to a queue or rather than doing CRUD operation on a database we might need to use a different AWS resource like ElasticSearch. To cater to these kinds of scenarios we can use AWS lambda functions as our Data source.
For this article let’s assume that rather than taking user data from our DynamoDb we want to take them from an AWS lambda function. For that first, let’s create our lambda function. The query that we are going to map as a resolver is
getUser(id: Int!): User
Create a new lambda function and add the below code for the function. Here we are checking for the event.field parameter which we will send through the resolver. So if the field parameter is getUsers then we will return the filtered user.
Now let’s configure a resolver for this function. Before that, we need to register this function as a data source in our API. For that go to the Data Sources tab and click on Create data source.
Next, provide a name for the data source, select DataStorageType as Lambda, and then region and lastly the lambda function we created.
The next step is to assign this data source in our schema. For that first delete the resolver already mapped to getUser() when we mapped our schema to DynamoDB table.
After deleting click on Attach. Then select the data source as the data source we created for our lambda function. Next will be to add templates for request mappings and response mappings.
{    "version": "2017-02-28",    "operation": "Invoke",    "payload": {        "field": "getUser",        "arguments":  $utils.toJson($context.arguments)    }}
Add the above as a request mapping template. Here we specify the field as getUser which we use in our lambda function as event.field. For the response mapping, we can leave it as it is.
Now let’s try to query this from our GraphQL API.
That’s it. Even though here the lambda function is used to just return a user I think you might be able to figure out whatever a lambda function can do can be mapped to our GraphQL as well, which indeed creates a serverless API.
I think you have learned more about what GraphQL and how to use AWS AppSync to create a GraphQL for us. There are many more concepts that have not been covered in this article. So if you are keen on this make sure to follow official documentation as well as other awesome articles that are available out there. Thank you.