This chapter is included in the free preview

Why AppSync

Before we dive into the details of GraphQL and AppSync, let's first see how such an API works! In this chapter, we'll deploy a social network built with technologies we'll look into in the rest of the book and see the distinguishing points why a GraphQL-based solution brings benefits.

We'll build this application step-by-step in the Example application chapter.

Follow along

Download the code from here, deploy in your own account, and follow this chapter first-hand.

Deploy

With the code downloaded, Terraform installed, and the AWS CLI configured, then everything is ready to deploy the app!

First, have Terraform download the modules it needs:

$ terraform init

Initializing the backend...

Initializing provider plugins...
...

Terraform has been successfully initialized!
...

Next, deploy:

$ terraform apply

...

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

It takes 5-10 minutes for everything to deploy, but then Terraform outputs the URL of the frontend:

...

aws_cloudfront_distribution.distribution: Creation complete ...
data.aws_iam_policy_document.s3_policy: Reading...
data.aws_iam_policy_document.s3_policy: Read complete after  ...
aws_cognito_user_pool_client.client: Creating...
aws_s3_bucket_policy.oac: Creating...
aws_cognito_user_pool_client.client: Creation complete after  ...
aws_s3_object.frontend_config: Creating...
aws_s3_object.frontend_config: Creation complete after 0s  ...
aws_s3_bucket_policy.oac: Creation complete after 1s  ...

Apply complete! Resources: 179 added, 0 changed, 0 destroyed.

Outputs:

domain = "d3d5eepwft4kat.cloudfront.net"

Open it in a browser and you'll be greeted with the main page:

The main page of the example app

Log in with the user:

Login page

The app shows the current user's posts, the comments for them, and the list of friends:

The user's feed with the posts and friends lists

Then when clicking on the username of another user, its feed is shown:

A different user's feed

Open the devtools

So far there is nothing an app with a different technology stack couldn't do. To find the difference, we'll need to look under the hood.

On page load, there are only two dynamic requests:

Only 2 requests are sent on page load

Looking into the first one, we see a complex query:

{
  "query":"
    query MyQuery($friendsNextToken: String, $id: ID!) {
      currentUser {
        id
        name
        avatar
      }
      user(id: $id) {
        id
        name
        avatar
        posts {
          # ...
        }
        # ...
      }
      # ...
    }
  ",
  "operationName":"MyQuery",
  "variables":{"id":"550aa038-1cb0-4ef8-a085-bf6dedfda8a0"}
}

The request defines several fields to get from the backend. Then these are present in the response:

{
  "data": {
    "currentUser": {
      "id": "a7ded6f4-11c7-4f0d-aa48-57afb64f9bf6",
      "name": "Test User",
      "avatar": "..."
    },
    "user": {
      "id": "550aa038-1cb0-4ef8-a085-bf6dedfda8a0",
      "name": "Lisa Ondricka",
      "avatar": "...",
      "posts": {
        "nextToken": "...",
        "posts": [
          // ...
        ]
      }
    }
  }
}

This provides an efficient way to communicate between the frontend and the backend: by sending a complex query the client can define exactly what it needs and the backend prepares the response without wasting roundtrips. Roundtrips are the slowest part of a frontend, and minimizing them provides an enormous speedup for webapps.

Unlike the backend-for-frontend approach where the backend provides a special endpoint for each client where each endpoint returns only the data that client needs to display, GraphQL clients use a single API. This makes the backend development easier as it does not need to keep the client in mind: define what data is available and let the clients send queries with what they need.

And what's the second request? It's pagination, a thing that we'll cover later in the book.

Real-time updates

Looking around the in Network panel, you can find another interesting bit: a WebSocket connection that sends new posts and comments in real-time:

Real-time updates via a WebSocket channel

Looking further, we'll see the same familiar syntax for real-time updates as for synchronous queries:

{
  "query":"
subscription MySubscription($userId: ID!) {
  post(userId: $userId) {
    post {
      date
      id
      text
    }
  }
}
  ",
  "variables":{"userId":"550aa038-1cb0-4ef8-a085-bf6dedfda8a0"}
}

AppSync provides this WebSocket endpoint and also handles all connections and sending messages to clients. This makes the solution trivially easy to operate: it works the same with one client as with a thousand.

And a shared WebSocket channel also makes things lighter on the frontend: in the ideal case, a webapp requires only one fetch and one WebSocket connection. No extra roundtrips, no additional connections.

Backend

After the client-side benefits, let's see what resources we have on the AWS-side!

There is an AppSync API, with a schema and the implementations of the various parts required according to GraphQL:

An AppSync API is deployed on AWS

The API integrates with DynamoDB to read and write data:

AppSync integrates with DynamoDB

And users are stored in a Cognito User Pool:

Users are stored in a Cognito User Pool

This provides a truly serverless solution: no part requires managing instances or planning capacity. AppSync integrates with many AWS services, making it possible to run Lambda functions to serve queries, or make HTTP requests in response to data changes. We'll discuss how in the book.

Cleanup

When you're done examining the example project, don't forget to remove it:

$ terraform destroy

...

Plan: 0 to add, 0 to change, 179 to destroy.

Changes to Outputs:
  - domain = "d3d5eepwft4kat.cloudfront.net" -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure,
  as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

...

random_id.id: Destroying... [id=sqGOdyTlSow]
random_id.id: Destruction complete after 0s
aws_s3_bucket.frontend_bucket: Destruction complete after 1s

Destroy complete! Resources: 179 destroyed.