Ionic 4 + AppSync: Create a GraphQL API - Part 2

In this post, we'll have a look at how to set up the AWS AppSync API which will be used in our Ionic app. We'll create a simple Quiz app that lets you create Decks and Cards to showcase the CRUDL functionality that is supported by the AppSync GraphQL API.

This tutorial is split up into these parts:
Part 1 - Introduction to GraphQL & AWS AppSync
Part 2 - Create an AWS AppSync API (this post)
Part 3 - Add Ionic pages and GraphQL queries
Part 4 - Use GraphQL mutations in Ionic
Part 5 - Set up User Authentication (coming soon)
(more parts will be added later)

Sign up for AWS account

First, you'll need to sign up for an AWS account, you'll need a credit card and a valid phone number. After sign up, you'll receive an automated call from AWS to verify your phone number.

Your credit card will only be charged if you go over the free tiers for the services you're using. For now we will be using the following AWS services:

Please be aware that if you follow this tutorial you will not only be using AppSync, but other AWS services as well. Each service has its own pricing scheme and generous free tiers, some of which expire after 12 months. The backend we're building with this tutorial shouldn't bring you over the limits of the free tiers, but make sure you familiarize yourself with the pricing information to avoid any surprises.

Install Amplify CLI

There are a couple of different ways to create an AppSync API, you can do it by logging into the AWS Console in a browser and clicking your way through the interface or you can do it with the AWS Amplify CLI, which will make your life a lot easier, so let's go ahead and install the CLI.

$ npm install -g @aws-amplify/cli

Install Ionic CLI

We'll need the Ionic CLI to create our Ionic 4 app, so install that if you haven't already.

$ npm install -g ionic@latest

Configure Amplify

We're now going to configure the access credentials for the Amplify CLI to manage our AWS backend.

$ amplify configure

This will open up your browser and take you to the AWS Console where you can log in with your AWS account. You should see the following output in the CLI.

Follow these steps to set up access to your AWS account:

Sign in to your AWS account:  
https://console.aws.amazon.com/  
Press Enter to continue  

After you're signed in, go back to the terminal and press Enter to continue the configuration.

The next question it will ask is to choose a region. A region is a geographic area where AWS has physical data centers. If most of the users of your app are located within a specific region you can choose that region for your backend to reduce latency. Read more about choosing regions.

For this tutorial I've chosen eu-west-1 (Ireland). You can find a list of all the regions on the AWS website.

Specify the AWS Region  
? region:  eu-west-1

Next question: name of Amplify user (choose a name or press Enter for a generated name).

Specify the username of the new IAM user:  
? user name:  amplify-anQ7X

This will take you to the Console again, select Programmatic Access and click on the button Next: Permissions. On the next screen, Administrator Access will be selected for this user, don't change anything there and click on Next:Review and then on Create user.

You'll see the Access key ID and Secret access key for this user, copy these and go back to the CLI and press Enter and paste the keys when prompted.

Enter the access key of the newly created user:  
? accessKeyId:  AKIAJ6TZMM**********
? secretAccessKey:  b/MY3YmWKfmsHMwoPYg1********************

Final step: specify a profile name for the configuration we have just set up. I'm naming mine gonehybrid.

This would update/create the AWS Profile in your local machine  
? Profile Name:  gonehybrid

We have now set up the user which will be used by the Amplify CLI to deploy our AWS resources. You only have to do this once, the profile that was created at the end can now be used in the apps you're going to build with Amplify.

It's now time to set up our Ionic 4 app.

Start Ionic app

Create the Ionic 4 app with the following command. Please note that Ionic 4 is currently in beta.

$ ionic start quiz-app blank --type=angular

You'll get a question about the Ionic Pro SDK, we're not going to use it in this tutorial, just choose No.

Your Ionic app is now created, make sure you go to the root of the app:

$ cd quiz-app

Initialize Amplify

We are going to initialize Amplify for our app.

$ amplify init

Choose the options as outlined below, my editor is Visual Studio Code, but you can choose your default editor if you're using something else.

Note that at the end it asks which profile you want to use, you should be able to select the profile you just created.

? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project  
? What javascript framework are you using ionic
? Source Directory Path:  src
? Distribution Directory Path: www
? Build Command:  npm run-script build
? Start Command: ionic serve
Using default provider awscloudformation

For more information on AWS Profiles, see:  
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use gonehybrid

Amplify will now connect to AWS and set up the resources it needs to do deployments.

Let's continue and add our AppSync GraphQL API.

Create AppSync API

$ amplify add api

Choose the options as outlined below, we will use an API key for now, later on we'll replace it with user authentication using Amazon Cognito. API keys are only meant to be used for development, and they're only valid for 7 days, but you can renew them.

? Please select from one of the below mentioned services GraphQL
? Provide API name: quizapp
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? y
? What best describes your project: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)
? Do you want to edit the schema now? Yes

At this point it should open your default editor with an example of a GraphQL schema, we're going to delete everything and define our own schema as below.

type Deck @model {  
  id: ID!
  name: String!
  cards: [Card]! @connection(name: "DeckCards")
}

type Card @model {  
  id: ID!
  question: String!
  answer: String
  deck: Deck! @connection(name: "DeckCards")
}

As you can see it's pretty straightforward to create the types and their fields. This is just a small part of the GraphQL schema, because a complete GrahpQL schema will also define which queries and mutations you can use. We're not going to create those ourselves, but we'll let Amplify generate them for us.

The ID type means that it's a unique string, the exclamation mark means that it's a required field. For a list on all the scalar types you can use in AppSync have a look here.

You can see that we're also using our own types for the fields: the cards field on type Deck is an array of Cards and the deck field on type Card references one Deck.

The exclamation mark following an array means that you can always expect an array with zero or more items.

We are using two directives here that are not part of the GraphQL specification but are used by Amplify to set up the DynamoDB tables for the types in the schema.

@model tells Amplify that it needs to generate DynamoDB tables for the type.

@connection specifies the relationship between different types. Based on this, Amplify will generate a mutation on the GraphQL schema to create the Card. This mutation will take as input the fields of the Card: question and answer, and the id of the Deck. These fields will then be saved into the Card table on DynamoDB. The queries for Card and Deck will then use resolvers created by Amplify to get the related data from the DynamoDB tables. We'll have a look at these queries and mutations when we're done creating the API.

Once you're done with the schema, save it, go back to the CLI and press Enter. This will compile the schema and check if it's valid.

We can now deploy this API to AWS.

$ amplify push

Amplify will ask you if you're sure you want to continue and then it will prompt you with the following questions.

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target typescript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.ts
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter the file name for the generated code src/API.ts

Amplify will now create the AppSync API based on the GraphQL schema we defined and it will also create the DynamoDB tables for our GraphQL types. When that's done Amplify will display the GraphQL endpoint and GraphQL API KEY our app will need to connect to it.

Let's have a look at what's deployed

Log into the AWS Console and go to the AppSync service (choose Services in the menu on top).

Just a quick note here: if at any time you sign in to the AWS Console and can't see AWS resources you've created, it probably means that you're looking in the wrong region. You can change the region on the top right menu (next to your username).

We can see that the quizapp API has been created, if you click on it you can see the Schema which contains our Card and Deck types and a whole bunch of Queries and Mutations that were generated for us by Amplify.

type Mutation {  
    createDeck(input: CreateDeckInput!): Deck
    updateDeck(input: UpdateDeckInput!): Deck
    deleteDeck(input: DeleteDeckInput!): Deck
    createCard(input: CreateCardInput!): Card
    updateCard(input: UpdateCardInput!): Card
    deleteCard(input: DeleteCardInput!): Card
}

type Query {  
    getDeck(id: ID!): Deck
    listDecks(filter: ModelDeckFilterInput, limit: Int, nextToken: String): ModelDeckConnection
    getCard(id: ID!): Card
    listCards(filter: ModelCardFilterInput, limit: Int, nextToken: String): ModelCardConnection
}

Go to Data Sources (left menu) where you can see the DynamoDB tables that are connected to the GrahpQL API. Click on the links for CardTable and DeckTable to go to the DynamoDB Tables page where you can inspect the tables.

Important info: the DynamoDB tables are created with 5 read capacity units (RCU) and 5 write capacity units (WCU) by default. These are provisioned for reading and writing data from the table, meaning that once you go over the free tier (25 RCUs and 25 WCUs in total for all tables), AWS will charge you for the additional units regardless of whether you're actively using the database or not. So keep that in mind if you're creating more tables, you can always scale down the RCUs and WCUs on the tables by going to the Capacity tab.

Go to the Items tab, you can see that we don't have any items yet, so let's go ahead and use our GraphQL API to add some data.

Adding data with Mutations

Go back to the AppSync service and then to the Queries page (left menu) and type in the following mutation to add a new Deck:

mutation createDeck {  
  createDeck(input: {
    name: "AppSync"
  }) {
    id
  }
}

This mutation takes as input the name of the Deck and it sends back as output the id which was generated. You can define yourself which data you want the mutation to return, for now I'm only interested in the id, but I could also request other fields on the type Deck.

Press the big Play button on top or press Ctrl+Enter to execute this mutation. On the right side of the page you should see the result of the mutation.

So when we execute this mutation, we'll see that a unique id was automatically created for this Deck.

{
  "data": {
    "createDeck": {
      "id": "55993f99-1495-4ac0-9ba3-93cf4e68153b",
    }
  }
}

We can go and have a look at the DynamoDB table for this type, and you should see a new item in the Items tab there.

Now let's create a Card and add it to the Deck, the input for this mutation takes the id of the Deck to connect the Card to this Deck.

mutation createCard {  
  createCard(input: {
    cardDeckId: "55993f99-1495-4ac0-9ba3-93cf4e68153b"
    question: "What is DynamoDB?"
    answer: "DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability."
  }) {
    id
    question
    answer
  }
}

The output should like like this, we've asked to return the id of the Card and the question and answer.

{
  "data": {
    "createCard": {
      "id": "f3c87e06-c670-4fab-a1f0-d91594a0122b",
      "question": "What is DynamoDB?",
      "answer": "DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability."
    }
  }
}

If you have a look at the GraphQL schema again, you can see that there are mutations defined for updates and deletes as well. We will use all of these later in the app to do CRUD operations.

Reading data with Queries

Let's add a query now to get all the Decks and Cards, but let's say only we're only interested in the questions and not the answers.

query getDecksWithQuestions {  
  listDecks {
    items {
      name
      cards {
        items {
          question
        }
      }
    }
  }
}

When you execute the query you'll get the result below, I added a few more cards to my AppSync deck.

{
  "data": {
    "listDecks": {
      "items": [
        {
          "name": "AppSync",
          "cards": {
            "items": [
              {
                "question": "What is the difference between GraphQL and REST?"
              },
              {
                "question": "What is DynamoDB?"
              },
              {
                "question": "What is a region?"
              }
            ]
          }
        }
      ]
    }
  }
}

What's next?

We're done with the backend for now. We've set up our GraphQL schema, we know how to write queries and mutations and we have the endpoint to send our queries to.

In the next part, we'll build the views for our Ionic app and use Amplify to help us connect to the AppSync API.

If you have any questions or if you're having problems following the steps in this tutorial, please leave a comment below and I'll try to help you out.