Getting Started With AWS Amplify - Part One - Initialize Amplify

traviswerbelow

Travis Werbelow

Posted on August 1, 2020

Getting Started With AWS Amplify - Part One - Initialize Amplify

Originally posted on my personal blog here: travis.codes

I recently came across AWS Amplify and have really enjoyed playing around with it. I am trying to get better at blogging, and also wanted to teach myself more about Amplify, so this kills two birds with one stone.

In this 2+ part blog post series, I wanted to build a simple Todo AMA type app. I think the app concept covers a handful of concepts that will help you learn a bit more about Amplify.

Here is what we will be making:

Screenshot of the demo app

In this first post, I wanted to dive into getting started with Amplify and playing around in the GraphiQL playground with our API. In the next post, we will build out the UI and add Authentication.

This is one of my first bigger technical type posts, so if you find any mistakes, please nicely let me know 😅

Alrighty, let’s do this!

Install

First things first, let’s get all the things installed.

install all the things meme

npx create-react-app ama --use-npm
Enter fullscreen mode Exit fullscreen mode

Install our dependencies

cd ama
npm install aws-amplify node-sass react-router react-router-dom @aws-amplify/ui-react
Enter fullscreen mode Exit fullscreen mode

If you don’t already have the AWS Amplify CLI installed, do that now: Amplify Framework Documentation

Let’s get Amplify initiated with amplify init
You will be given a list of questions to answer.

image of terminal showing amplify init
For the most part, I chose the default. For the code editor, I use VS Code, but if you use something else, be sure to pick that.
Since we are using javascript, we pick that
And we are using react
The defaults for the paths and commands are what we want.
Then say Yes to using an AWS profile and chose the profile you want.
After that, Amplify will start initializing the project in the cloud.

Adding our API

We will be using the GraphQL Transform in order to get our backend set up quickly. We use the Schema Definition Language or SDL to model our data, and then the amplify converts our SDL into AWS CloudFormation templates for us, kind of like magic.

To get started just run amplify add api

Select GraphQL
Just use the default API name by pressing enter
For the default authorization, choose API key for now, we will revisit Auth in a future post.
Pick the defaults for the API key questions
Do you have an annotated GraphQL schema? No
Do you want a guided schema creation? Yes
What best describes your project? One-to-many relationship
Do you want to edit the schema now? Yes

All the questions and answers:
image of terminal showing amplify add API

Your code editor should now be open with the following schema:

type Blog @model {
  id: ID!
  name: String!
  posts: [Post] @connection(keyName: "byBlog", fields: ["id"])
}

type Post @model @key(name: "byBlog", fields: ["blogID"]) {
  id: ID!
  title: String!
  blogID: ID!
  blog: Blog @connection(fields: ["blogID"])
  comments: [Comment] @connection(keyName: "byPost", fields: ["id"])
}

type Comment @model @key(name: "byPost", fields: ["postID", "content"]) {
  id: ID!
  postID: ID!
  post: Post @connection(fields: ["postID"])
  content: String!
}
Enter fullscreen mode Exit fullscreen mode

We are going to replace all of it, but this gives us a good starting point.

Let’s walk through what the above means.
Each object type has a couple words with the @ symbol in front of them, these are called directives and are super helpful in creating our API.

As of right now, Amplify has 9 built in directives.

@model
@key
@auth
@function
@connection
@versioned
@searchable
@predictions
@http
Enter fullscreen mode Exit fullscreen mode

In our example we are currently using @model, @connection and @key so let’s look into what those mean.

@model

Object types with the @model directive are stored in DynamoDB, can be protected with the @auth directive and can be searchable with the @searchable directive.

According to the docs, here is what Amplify is doing for us just by using the @model directive 🤯:

  • An Amazon DynamoDB table with PAY_PER_REQUEST billing mode enabled by default.
  • An AWS AppSync DataSource configured to access the table above.
  • An AWS IAM role attached to the DataSource that allows AWS AppSync to call the above table on your behalf.
  • Up to 8 resolvers (create, update, delete, get, list, onCreate, onUpdate, onDelete) but this is configurable via the queries, mutations, and subscriptions arguments on the @model directive.
  • Input objects for create, update, and delete mutations.
  • Filter input objects that allow you to filter objects in list queries and connection fields.
  • For list queries the default number of objects returned is 100. You can override this behavior by setting the limit argument.

@connection

The connection directive allows you to set up relationships between @model types. It currently supports one-to-one, one-to-many and many-to-one relationships.

@key

Makes it easy to configure custom index structures for @model types.
The @key directive has one required argument and two optional args

Fields
The list of fields that comprise the@key, used in conjunction with @model The first fields in the array will be the HASH key, if a second field is provided, it is used as the SORT key.

Name
If provided, the name provides the name of the secondary index.

queryField
This allows you to specify a new top level query that uses the secondary index, defined by setting the name argument.

For great examples and data patterns, check out Amplify Framework Documentation

User stories

Now that we know a little bit more about what our schema is doing, let’s start making our own schema, but first let’s create some user stories.

  • Users can see a list of all questions
  • Users can ask a new question
  • A user can answer a question
  • A user can delete a question

Now that we know what our app will be doing, let’s model out our data. Replace all of the generated Todo Schema and replace it with the following:

type Question
  @model
  @key(
    name: "byDate"
    fields: ["type", "createdAt"]
    queryField: "questionsByDate"
  ) {
  id: ID!
  type: PostType!
  content: String
  createdAt: AWSDateTime!
  answer: Answer @connection(fields: ["id"])
}

type Answer
  @model
  @key(fields: ["questionID"])
 {
  id: ID!
  questionID: ID!
  content: String!
  createdAt: AWSDateTime!
}

enum PostType {
  QUESTION
}

Enter fullscreen mode Exit fullscreen mode

The @key directive for the Question type, allows us to query our Questions by type, and then sort by createdAt
The @connection directive on the Question answer property, creates a relationship between the Question and the Answer models. Each Question can only have one Answer.
The @key directive on the Answer model creates a bi-directional one-to-one relationship with our Question.

Once you have it how we like it, save the file and go back to your terminal and press enter. It will most likely yell as you for not having the @auth directive on our models, but we will cover that in the next post.

This will generate our API, now we can do an amplify push to deploy it.

You’ll see a status of what resources have been created/updated, and then you can hit enter to continue.

image of terminal showing amplify push status

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/
**/*.js # default
? Do you want to generate/update all possible GraphQL operations - queries, mutations and
subscriptions Yes #default
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2 #default
Enter fullscreen mode Exit fullscreen mode

Alternately, you can run amplify push -y to answer Yes to all questions.

Once you go through the questions, you’ll see an Updating resources in the cloud. This may take a few minutes... message.

Wait a couple of minutes, and you should have your API deployed! 🎉

Ron Swanson GIF

Testing our API

The Amplify CLI has a handy feature that allows us to mock our API, for testing locally. Just run amplify mock api It will spit out a url for you to use which will open a GraphiQL interface in which we can test at our API.

If you haven’t used GraphiQL before, it’s pretty straight forward. On the left-hand side you’ll see the queries that Amplify made for us. On the bottom of the left panel, you can switch between Query, Mutation, and Subscription.

GraphiQL Interface

The main panel is split into two sections, the left side is where we write our queries, mutations, and subscriptions, and the right side is what gets returned. We also have a docs panel on the upper right we can open. This shows us all the available functions we have, the inputs, and more.

Let’s dive in and start playing around with our API.

createQuestion mutation

In order to get some data added in, let’s create our first question.

mutation createQuestion {
  createQuestion(input: {
    content: "do you like bacon and eggs?"
    type: QUESTION
  }) {
    id
    content
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we are returning the id from the newly created Question.

The data returned should look like this:

{
  "data": {
    "createQuestion": {
      "id": "46bcc95a-4457-4dd6-b33a-e286ed049cf8",
      "content": "do you like bacon and eggs?"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

We can add our questionID and our answerContent to our variables panel so we can answer a question in our GraphiQL playground. Once we do so, we can write our createAnswer mutation.

image of GraphiQL playground query variables

createAnswer mutation

mutation createAnswer($questionID: ID!, $answerContent: String!) {
  createAnswer(input:{
    questionID: $questionID
    content: $answerContent
  }) {
    id
    content
  }
}
Enter fullscreen mode Exit fullscreen mode

This will return data that looks like this:

{
  "data": {
    "createAnswer": {
      "id": "c6ac3607-5995-4c55-80f3-e90d5a106a03",
      "content": "I do, and I will take all the bacon and eggs you have"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

listQuestions query

To see if that all worked, lets do a query and list all the questions with their answer

query listQuestions {
  listQuestions {
    items {
      id
      content
      answer {
        id
        content
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

If you have only added the one question above, you should see something similar to this:

{
  "data": {
    "listQuestions": {
      "items": [
        {
          "id": "3336596f-6e5a-488b-a0dd-6ebe1699cf54",
          "content": "do you like bacon and eggs?",
          "answer": {
            "id": "d456152a-e995-49ce-ab4f-2d28ba2dc99a",
            "content": "I do, and I will take all the bacon and eggs you have"
          }
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

deleteQuestion mutation

One last thing, we should be able to delete a question.

mutation deleteQuestion($questionID: ID!) {
  deleteQuestion(input: {id: $questionID}) {
    content
  }
}
Enter fullscreen mode Exit fullscreen mode

This will delete the question with the passed questionID. To double check it worked, you can run the list questions query and it should return an empty items array.

{
  "data": {
    "listQuestions": {
      "items": []
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Whew, I think we made it! We got Amplify set up, we added a GraphQL API and then created some fake data using the GraphiQL playground.

In the next post, we will create the React app that will handle asking a question, answering a question.

👉 Part Two

You can find the code here:

GitHub logo Werbelow / amplify-tutorial

A getting started with Amplify tutorial blog post series

💖 💪 🙅 🚩
traviswerbelow
Travis Werbelow

Posted on August 1, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related