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.
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 of the item
id: ID!
# id of the list to which this item belongs to
list_id: ID!
# name of the item
name: String!
# description of the item
description: String
# due date of this item
date: String
# whether this item has been marked as done or not.
status: Boolean!
list: List
}
type List {
# id of the list
id: ID!
# id of the user to whom this list belongs to
user_id: ID!
# name of the list
name: String!
# description of the list
description: String
# all the items associated with this list
items: [Item]
user: User
}
type Mutation {
# this will add a new user in database
createUser(
name: String!,
email: String!,
password: String!,
phone: String,
address: String
): User
# this will update existing user in the database
updateUser(
id: ID!,
name: String,
email: String,
password: String,
phone: String,
address: String
): User
# this will add a new list in database
createList(user_id: ID!, name: String!, description: String): List
# this will update existing list
updateList(id: ID!, name: String!, description: String): List
# this will delete a list and all of its items
deleteList(id: ID!): List
# this will add an item in the database
createItem(
list_id: ID!,
name: String!,
description: String,
date: String,
completed: Boolean
): Item
# this will update the item in database
updateItem(
id: ID!,
list_id: ID,
name: String,
description: String,
date: String,
completed: Boolean
): Item
# this will delete the item from database
deleteItem(id: ID!): Item
#Put a single value of type 'User'.
########## If an item exists it's updated. If it does not it's created.
putUser(
id: ID!,
name: String!,
email: String!,
phone: String,
address: String
): User
#Delete a single value of type 'User' by a primary key.
deleteUser(id: ID!): User
#Put a single value of type 'List'.
###### If an item exists it's updated. If it does not it's created.
putList(
id: ID!,
user_id: ID!,
name: String!,
description: String
): List
#Put a single value of type 'Item'.
###### If an item exists it's updated. If it does not it's created.
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
#Get a single value of type 'User' by primary key.
getUser(id: ID!): User
#Scan through all values of type 'User'.
########## Use the 'count' and 'nextToken' arguments to paginate.
allUser(count: Int, nextToken: String): [User]
#Get a single value of type 'List' by primary key.
getList(id: ID!): List
#Scan through all values of type 'List'.
###### Use the 'count' and 'nextToken' arguments to paginate.
allList(count: Int, nextToken: String): [List]
#Get a single value of type 'Item' by primary key.
getItem(id: ID!): Item
#Scan through all values of type 'Item'.
###### Use the 'count' and 'nextToken' arguments to paginate.
allItem(count: Int, nextToken: String): [Item]
}
type User {
# id of the user
id: ID!
# name of the user
name: String!
# email of the user
email: String!
# phone number of the user
phone: String
# address of the user
address: String
# all the lists associated with this user.
lists: [List]
}
schema {
query: Query
mutation: Mutation
}
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 recordPutItem
: to insert new recordUpdateItem
: for updating the recordDeleteItem
: for deleting recordScan
: finding multiple recordsQuery
: finding multiple recordsI 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...
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)
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)
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
user_id
1
1
Finally create on Create index button, Wait for few seconds and the new index will be added for this table.
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)
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.
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": []
}
]
}
}
}
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": []
}
]
}
]
}
}
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.
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.
December 21, 2017 at 02:30 PM
and is written by Dhruv Kumar Jha (me).