We designed our schema, created resources, attached resolvers, tested mutations + queries and everything seems to be working fine.

But everything separated.

To find all Users we run the users query, to find all Lists we run the lists query and to find all Items, we run the items query.

Wouldn't it be better if we can ask for items within the lists query itself or ask it lists from within the user query itself?

Yes we can, that's the beauty of GraphQL. Let's do that.

Updating our Schema

As always open the API we created in AWS AppSync, In there go to Schema page and make the following changes.

Let's update our type List to include items field.

type List { ... items : [ Item ] }

Let's update our type User to include lists field.

type User { ... lists : [ List ] }

And while we're at it, Let's update item as well as List type so we can fetch additional info if required.

type Item { ... list : List }

type List { ... user : User }

This will allow us to find list details from within the item query and Allow us to find user details from within the list query.

This is our final Schema code

type Item { id : ID ! list_id : ID ! name : String ! description : String date : String status : Boolean ! list : List } type List { id : ID ! user_id : ID ! name : String ! description : String items : [ Item ] user : User } type Mutation { createUser ( name : String ! , email : String ! , password : String ! , phone : String , address : String ) : User updateUser ( id : ID ! , name : String , email : String , password : String , phone : String , address : String ) : User createList ( user_id : ID ! , name : String ! , description : String ) : List updateList ( id : ID ! , name : String ! , description : String ) : List deleteList ( id : ID ! ) : List createItem ( list_id : ID ! , name : String ! , description : String , date : String , completed : Boolean ) : Item updateItem ( id : ID ! , list_id : ID , name : String , description : String , date : String , completed : Boolean ) : Item deleteItem ( id : ID ! ) : Item putUser ( id : ID ! , name : String ! , email : String ! , phone : String , address : String ) : User deleteUser ( id : ID ! ) : User putList ( id : ID ! , user_id : ID ! , name : String ! , description : String ) : List putItem ( id : ID ! , list_id : ID ! , name : String ! , description : String , date : String , status : Boolean ! ) : Item } type Query { users : [ User ] user ( id : ID ! ) : User lists : [ List ] list ( id : ID ! ) : List items : [ Item ] item ( id : ID ! ) : Item getUser ( id : ID ! ) : User allUser ( count : Int , nextToken : String ) : [ User ] getList ( id : ID ! ) : List allList ( count : Int , nextToken : String ) : [ List ] getItem ( id : ID ! ) : Item allItem ( count : Int , nextToken : String ) : [ Item ] } type User { id : ID ! name : String ! email : String ! phone : String address : String lists : [ List ] } schema { query : Query mutation : Mutation }

Different ways of getting data in DynamoDB

There are many ways in which we can get the data we want, We set the method in the operation field within our request mapping template .

GetItem : to find single record

: to find single record PutItem : to insert new record

: to insert new record UpdateItem : for updating the record

: for updating the record DeleteItem : for deleting record

: for deleting record Scan : finding multiple records

: finding multiple records Query : finding multiple records

I am not highly experienced in DynamoDB, That aside Scan and Query can be used for finding records matching our conditions and filters.

According to their documentation

The Query operation finds items based on primary key values. You can query any table or secondary index that has a composite primary key (a partition key and a sort key).

A Scan operation reads every item in a table or a secondary index. By default, a Scan operation returns all of the data attributes for every item in the table or index. You can use the ProjectionExpression parameter so that Scan only returns some of the attributes, rather than all of them.

There are many other factors that will come in play when choosing one over other like Speed, Cost, etc. So I will leave this decision upto you.

We will use both of these below so you can see how it's implemented.

To access parents field instead of the arguements we pass via GraphQL, We can make use of "${context.source.FIELD_NAME}" ., I don't think this is documented anywhere...

Attaching Resolver for items: [Item] field for Type List

Open Schema page, In the Data Types section search for List, Click on Attach button next to field items: [Item]

For Data source name select ItemTable

For Configure the request mapping template add in this code

{ "version" : "2017-02-28" , "operation" : "Scan" , "filter" : { "expression" : "list_id = :list_id" , "expressionValues" : { ":list_id" : { "S" : "${context.source.id}" } } } }

What does this code do?

We're saying find all documents where list_id field value is same as id field value of it's source , i..e item.list_id === list.id

And for Configure the response mapping template code, Add this code

$utils.toJson($context.result.items )

Attaching Resolver for lists: [List] field for Type User

Open Schema page, In the Data Types section search for User, Click on Attach button next to field lists: [List]

For Data source name select ListTable

For Configure the request mapping template add in this code

{ "version" : "2017-02-28" , "operation" : "Query" , "index" : "user_id-index" , "query" : { ## Provide a query expression. ** "expression" : "user_id = :user_id" , "expressionValues" : { ":user_id" : { "S" : "${context.source.id}" } } } }

Here we're using Query operation, If we don't specify the primary key : id It will fail., But we can create a new index on different key and use that.

Here we're just asking the DynamoDB to make use of user_id-index Index. And find all records where user_id field === context.source.id i...e user.id

And for Configure the response mapping template code, Add this code

$utils.toJson($context.result.items )

Let's not forget to create new index for field user_id

Open AWS Dynamo DB, Click on Tables link in the left Sidebar.

Once you see all the tables, Select ListTable and it that page goto Indexex tab.

There click on Create index button, It will open a popup, Enter these values for those fields

Primary key : Enter user_id

: Enter Index name : As soon as you enter primary key, It will auto update this field, but you can change the name to whatever you want, I have left it as it is.

: As soon as you enter primary key, It will auto update this field, but you can change the name to whatever you want, I have left it as it is. Read capacity units : I chose 1

: I chose Write capacity units: I chose 1

Finally create on Create index button, Wait for few seconds and the new index will be added for this table.

Attaching Resolver for list: List field for Type Item

Open Schema page, In the Data Types section search for Item, Click on Attach button next to field list: List

For Data source name select ListTable

For Configure the request mapping template add in this code

{ "version" : "2017-02-28" , "operation" : "GetItem" , "key" : { ## If your table's hash key is not named 'id' , update it here. ** "id" : { "S" : "${context.source.list_id}" } ## If your table has a sort key , add it as an item here. ** } }

And for Configure the response mapping template code, Add this code

$util.toJson($context.result )

Attaching Resolver for user: User field for Type List

Open Schema page, In the Data Types section search for List, Click on Attach button next to field user: User

For Data source name select UserTable

For Configure the request mapping template add in this code

{ "version" : "2017-02-28" , "operation" : "GetItem" , "key" : { ## If your table's hash key is not named 'id' , update it here. ** "id" : { "S" : "${context.source.user_id}" } ## If your table has a sort key , add it as an item here. ** } }

And for Configure the response mapping template code, Add this code

$util.toJson($context.result )

And with this we're done.

Testing

Open Queries page and run the following query

query { items { id name date list { id name description } } }

And we get the following response.

{ "data" : { "items" : [ { "id" : "81dcb364-de5e-43c0-a66b-913dd87b7c83" , "name" : "Awesome GraphQL" , "date" : "2017-12-20" , "list" : { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" } } , { "id" : "f8bafed8-fa57-438b-b8c1-95acaf375998" , "name" : "AWS AppSync" , "date" : "2017-12-20" , "list" : { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" } } , { "id" : "aae71b55-22f0-4cd5-bfc6-da870bdb5544" , "name" : "React JS" , "date" : "2017-12-20" , "list" : { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" } } ] } }

This is good, Now let's run an advanced Query, This will be very useful to us as we can get all the data we need in a single request.

query { user ( id : "5b0fc184-f11d-4342-ab4f-95721e9ee831" ) { id name email lists { id user_id name description items { id name description } } } }

And again we get the correct data.

{ "data" : { "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" , "email" : "dhruv@internet.com" , "lists" : [ { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" , "items" : [ { "id" : "81dcb364-de5e-43c0-a66b-913dd87b7c83" , "name" : "Awesome GraphQL" , "description" : "GraphQL is a data query language developed internally by Facebook in 2012 before being publicly released in 2015. It provides an alternative to REST and ad-hoc webservice architectures." } , { "id" : "f8bafed8-fa57-438b-b8c1-95acaf375998" , "name" : "AWS AppSync" , "description" : "AWS AppSync automatically updates the data in web and mobile applications in real time, and updates data for offline users as soon as they reconnect. AppSync makes it easy to build collaborative mobile and web applications that deliver responsive, collaborative user experiences." } , { "id" : "aae71b55-22f0-4cd5-bfc6-da870bdb5544" , "name" : "React JS" , "description" : "In computing, React is a JavaScript library for building user interfaces. It is maintained by Facebook, Instagram and a community of individual developers and corporations" } ] } , { "id" : "bee5f479-e875-4090-8de4-704f0ba746c1" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Awesome Tutorial Ideas" , "description" : "These are some of the tutorial ideas I have" , "items" : [ ] } , { "id" : "319226f5-9600-46b9-9dea-f9c80e4c5db7" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Favourite Websites" , "description" : "These are my Favorite Websites" , "items" : [ ] } ] } } }

Now let's just get crazy with our Query

query { users { id name email lists { id user_id name description user { id name email lists { id name description } } items { id name description list { id name description user { id name } } } } } }

It still works, Awesome.

{ "data" : { "users" : [ { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" , "email" : "dhruv@internet.com" , "lists" : [ { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" , "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" , "email" : "dhruv@internet.com" , "lists" : [ { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" } , { "id" : "bee5f479-e875-4090-8de4-704f0ba746c1" , "name" : "Awesome Tutorial Ideas" , "description" : "These are some of the tutorial ideas I have" } , { "id" : "319226f5-9600-46b9-9dea-f9c80e4c5db7" , "name" : "Favourite Websites" , "description" : "These are my Favorite Websites" } ] } , "items" : [ { "id" : "81dcb364-de5e-43c0-a66b-913dd87b7c83" , "name" : "Awesome GraphQL" , "description" : "GraphQL is a data query language developed internally by Facebook in 2012 before being publicly released in 2015. It provides an alternative to REST and ad-hoc webservice architectures." , "list" : { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" , "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" } } } , { "id" : "f8bafed8-fa57-438b-b8c1-95acaf375998" , "name" : "AWS AppSync" , "description" : "AWS AppSync automatically updates the data in web and mobile applications in real time, and updates data for offline users as soon as they reconnect. AppSync makes it easy to build collaborative mobile and web applications that deliver responsive, collaborative user experiences." , "list" : { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" , "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" } } } , { "id" : "aae71b55-22f0-4cd5-bfc6-da870bdb5544" , "name" : "React JS" , "description" : "In computing, React is a JavaScript library for building user interfaces. It is maintained by Facebook, Instagram and a community of individual developers and corporations" , "list" : { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" , "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" } } } ] } , { "id" : "bee5f479-e875-4090-8de4-704f0ba746c1" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Awesome Tutorial Ideas" , "description" : "These are some of the tutorial ideas I have" , "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" , "email" : "dhruv@internet.com" , "lists" : [ { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" } , { "id" : "bee5f479-e875-4090-8de4-704f0ba746c1" , "name" : "Awesome Tutorial Ideas" , "description" : "These are some of the tutorial ideas I have" } , { "id" : "319226f5-9600-46b9-9dea-f9c80e4c5db7" , "name" : "Favourite Websites" , "description" : "These are my Favorite Websites" } ] } , "items" : [ ] } , { "id" : "319226f5-9600-46b9-9dea-f9c80e4c5db7" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Favourite Websites" , "description" : "These are my Favorite Websites" , "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" , "email" : "dhruv@internet.com" , "lists" : [ { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" } , { "id" : "bee5f479-e875-4090-8de4-704f0ba746c1" , "name" : "Awesome Tutorial Ideas" , "description" : "These are some of the tutorial ideas I have" } , { "id" : "319226f5-9600-46b9-9dea-f9c80e4c5db7" , "name" : "Favourite Websites" , "description" : "These are my Favorite Websites" } ] } , "items" : [ ] } ] } , { "id" : "f20057e6-0a78-4abb-b6c2-9f3d29d83d90" , "name" : "John Doe" , "email" : "john.doe@gmail.com" , "lists" : [ { "id" : "656e99e5-0ce6-4f5e-b59b-6c6c0b4405f4" , "user_id" : "f20057e6-0a78-4abb-b6c2-9f3d29d83d90" , "name" : "John Doe's List" , "description" : "My awesome list of random things." , "user" : { "id" : "f20057e6-0a78-4abb-b6c2-9f3d29d83d90" , "name" : "John Doe" , "email" : "john.doe@gmail.com" , "lists" : [ { "id" : "656e99e5-0ce6-4f5e-b59b-6c6c0b4405f4" , "name" : "John Doe's List" , "description" : "My awesome list of random things." } ] } , "items" : [ ] } ] } ] } }

Testing with HTTP Clients:

Up until this point we've used AppSync Queries page to test our queries. Let's get our of AWS and test our queries with HTTP clients.

You can use any HTTP client, I will be using PostMan to test our query.

Do note we cannot use of GraphQL queries as it with these clients, We have to escape the quotes and new lines .

In AWS AppSync click on your API. This will open up your API details page.

Here we can see the API URL : https://icemqn5opzewdeer2s2isyg5su.appsync-api.us-east-1.amazonaws.com/graphql and API KEY : da1-abcdefghijklmnopsdhjdshjkf (I have changed my API key so this wont work.)

Whichever client you choose, Select

In Headers, Add new fields

x-api-key : da1-abcdefghijklmnopsdhjdshjkf

: Content-Type : application/json

In body, Paste in this RAW json (this is our query)

{ "query" : "query { user(id: \"5b0fc184-f11d-4342-ab4f-95721e9ee831\") { id name email lists { id user_id name description items { id name description } } } }" }

Hit Send to make the HTTPS Request and we get the response

{ "data" : { "user" : { "id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Dhruv" , "email" : "dhruv@internet.com" , "lists" : [ { "id" : "b35a92cf-c627-4831-95f2-2cb9f58594fb" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Favourite TechStack" , "description" : "These are my Favorite Tech" , "items" : [ { "id" : "81dcb364-de5e-43c0-a66b-913dd87b7c83" , "name" : "Awesome GraphQL" , "description" : "GraphQL is a data query language developed internally by Facebook in 2012 before being publicly released in 2015. It provides an alternative to REST and ad-hoc webservice architectures." } , { "id" : "f8bafed8-fa57-438b-b8c1-95acaf375998" , "name" : "AWS AppSync" , "description" : "AWS AppSync automatically updates the data in web and mobile applications in real time, and updates data for offline users as soon as they reconnect. AppSync makes it easy to build collaborative mobile and web applications that deliver responsive, collaborative user experiences." } , { "id" : "aae71b55-22f0-4cd5-bfc6-da870bdb5544" , "name" : "React JS" , "description" : "In computing, React is a JavaScript library for building user interfaces. It is maintained by Facebook, Instagram and a community of individual developers and corporations" } ] } , { "id" : "bee5f479-e875-4090-8de4-704f0ba746c1" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Awesome Tutorial Ideas" , "description" : "These are some of the tutorial ideas I have" , "items" : [ ] } , { "id" : "319226f5-9600-46b9-9dea-f9c80e4c5db7" , "user_id" : "5b0fc184-f11d-4342-ab4f-95721e9ee831" , "name" : "Favourite Websites" , "description" : "These are my Favorite Websites" , "items" : [ ] } ] } } }

If you encountered any errors/issues, Just let me know.

Conclusion

With this we're done (with the backend).

You can use the same API URL and API Key within React Project to communicate with the GraphQL Backend., And that's exactly what we will do next.