TM#015 — Building Scalable GraphQL APIs with Amplify — Data Modeling

Antematter
8 min readAug 23, 2023

Content

  • Background
  • GraphQL API and data modeling
  • Deploying the APIs
  • Mocking
  • Bye

Background

Caution: will try to sell you amplify

Lets start with a bit of background — AWS amplify is a purpose-built AWS service that lets us build scalable backend for mobile and web applications extremely fast.

Amplify:

  1. Lets us build Scalable backend because under the hood amplify uses AWS’ serverless services like Cognito for authentication, appsync for GraphQL API, pinpoint for analytics and so on. All the serverless services are built for scalability.
  2. Abstracts most of the complex stuff and handle most of the use cases out of the box — like in case of authentication, all you need is to run “amplify add auth” from the CLI and answer a couple of questions and the authentication is ready.
  3. If there are some use cases that are not handled out of the box, we can implement them using AWS cdk.
  4. We can export the stack using cdk or Cloudformation, so next time we just need a single command to deploy the full backend.
  5. Supports CICD for hosting and branched workflows for managing multiple environments for example cid, stg, uat, prd or whatever workflow/ number environments you have.
  6. Is built for setting up the backend extremely fast. As one of their customers said:

“We identified Amplify and Amplify Video as the fastest path to integrate video into our application. We estimated that to build and troubleshoot the solution on their own would have taken a full week of dedicated engineering and developer time. In contrast, by using the Amplify CLI and the video plugin code repository, we transcoded the video, generated signed urls, and deployed it to our development environment and then into production — all within an hour. We didn’t require any changes to our native iOS/Android code, we simply replaced the backend and Amplify did the work. Our team was able to solve for this complex challenge to keep our customers motivated and working out. We were very pleased to have found AWS Amplify and Amplify Video.”

source: https://aws.amazon.com/amplify/customers/

You can read more of their customer stories here as well.

Amplify supports both REST APIs (API Gateway under the hood) and GraphQL APIs (AWS Appsync) for making. We are going to discuss GraphQL API.

GraphQL API and data modeling

GraphQL consists of:

  1. Data model/ Schema — Queries, mutations, subscriptions, user defined types and relationships etc.
  2. Resolvers — Functions whose job is to give a query/mutation/subscription, figure out how modify or query the data from configured data sources.
  3. Data sources — It can be anything for example, MySQL database or maybe some API call.

In case of Amplify, setting up GraphQL API means:

  • Writing a schema with user defined types only

You need to give Amplify a GraphQL schema and it will:

  • Generate Queries, Mutation and Subscription types for it.
  • Write Resolver functions for you.
  • Set up DynamoDB as data source.
  • Create Tables in DynamoDB

and since its AWS it will

  • Let you use Cognito, API key and other methods of authorizations, like lambda, for the generated APIs — on Type level and even at the level of an attribute of a type.
  • Supports user groups as well.
  • Do bunch of ML related stuff like identifying text on image, identify labels on a image, translate text etc., using AWS defined directive: @predictions

All while allowing the ability to customize the behaviors of API by writing custom resolvers in JavaScript or VTL (advanced use cases, not needed mostly).

And the best part is

  • You can use Amplify Datastore with it as well. DataStore is local storage which sync the data to backend automatically — if you are doing some mutations and you don’t have internet it will make the changes locally and sync them when you connection is restored.
  • This is a powerful feature because it can minimize reload times — the data can be loaded from local storage initially and then updated, decreasing the user’s frustration.
  • And also, because you can make changes without worrying about you network connectivity, just keep modifying the data and it will automatically sync everything once you regain the connectivity.
  • Side note: DataStore can even be used without AWS account and amplify.

Data modeling

Now, lets move to Data modeling and the mocking the APIs.

We’ll be making API for a platform where users can brag about their fancy cars, post pictures of it and sell the cars as well by posting as Ad for it.

type User @model {
id: ID!
firstName: String!
lastName: String!
email: String!
blogs: [Blog!] @hasMany
comments: [Comment] @hasMany
}

type Blog @model {
id: ID!
name: String!
owner: User! @belongsTo
posts: [Post] @hasMany
}

type Post @model {
id: ID!
title: String!
blog: Blog @belongsTo
comments: [Comment] @hasMany
}

type Comment @model {
id: ID!
post: Post @belongsTo
content: String!
commentor: User! @belongsTo
}

We have defined a user here who can have many blogs, a blog can have many blog posts and other users can comment on the posts.

The user has hasMany relationship with blog and a blog has a owner with belongsTo relationship which makes in two-way one to many relationship - a user can have many blogs and a blog belong to a user. Two way relationship means we can get the user details from the blog itself.

We have relationship of same nature between blog and posts, posts and comments and user and comments.

Key words in schema are:

  1. ID is a unique identifier field. We can even omit this field and by default amplify will make ID field the primary key of table. We can modify this using @primaryKey directive on a field
  2. type — type keyword is used to define a “user defined data type”.
  3. @model is a aws defined directive which tells the Amplify that it needs to make a table in DynamoDB for this type
  4. @hasMany defines one-way one to many relationship on two types, in our case a Blog can have many posts.
  5. @belongsTo defines two-way relationship on two types, in our case a Blog can have many posts.

This is a very simple type of schema, for complex example: https://docs.amplify.aws/cli/graphql/examples-and-solutions/#warehouse-management-system

Deploying the API

In order to deploy the APIs on AWS, we need an AWS account and have the Amplify CLI installed and setup: https://docs.amplify.aws/cli/start/install/

Now, on your machine open up any IDE’s terminal like VS Code terminal or simple CMD and type in the following:

  • amplify init - this will initialize amplify project locally. (don’t worry we will push it later on). Answer the questions it asks and move to next step once project is setup
  • I went with JavaScript app type; you can choose any other from available.
  • amplify add api ; type should be GraphQL
  • Create a blank schema and choose default for rest.
  • Enter Yes on “Do you want to edit the schema now”, it will open the GraphQL schema file.
  • In case you entered No and now you can’t find the schema file it should be at amplify → backend → api → [api name] → schema.graphql. There will be same file in the build folder, we don’t need that one.
  • Copy paste the schema from above into this file and save.
  • Now, enter amplify push - this will deploy the api on backend.

Mocking

Amplify lets you mock the categories (services like API) locally. You can make changes locally, save the changes and they will the reflected in the mock server automatically. This speeds up the development process.

  • To mock GraphQL api enteramplify mock api.
  • Nothing fancy for now, just click enter for default stuff.
  • It should give you the link to mock server

Mock server

It’s given us a lot of APIs just from that schema and now let's run some of these.

Create a User

I have created a user simply by clicking the field I need for input and the fields I want returned from the left most panel :)

Just provide the required fields for user to create a user.

Create a Blog

Get the user’s id from last create user mutation and pass it here to userBlogsId. This is the relationship field for user and blogs which got autogenerated for you. Notice I have added the user info in returned fields as well. This is because of @belongsTo relationship, I can get the blogs from user and user from the blogs.

Create a Post

Get the user’s id from last create user mutation and pass it here to blogPostsId. This is the relationship field for blog and posts which got autogenerated for you. Same as above @belongsTo at work here.

Get user and blog

List Users

We DO NOT want to use list queries as they do full scan of DB tables and as your table grow, they will become cost expensive. But just for demonstration purposes:

We have user’s information, the blogs owned by this user (only one in our case) and posts that belong to each blog.

Query to display a Blog Post

Now with all mutations above to create user, blog, post and I added a comment as well, we can display a Blog Post given its id.

In the above query, we get Blog’s info to maybe display at the top of page, then the name of Blog Post as heading, I think we are missing the content of Post but it can be a simple attribute you guys can add and populate it and then we have comments information as well. We can show comments and who did the comment as well.

Bye

Now, our full working Blogs website is ready and it covers many of the normal use case.

We can model extremely complex use cases by making use of provided custom directives and everything.

https://docs.amplify.aws/cli/graphql/overview/

This article is written by Usama Hafeez, Full-Stack Engineer at Antematter.io.

--

--

Antematter

The world moves fast, your software should move faster. We develop and improve cutting-edge solutions using Blockchain & AI with maximum performance