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:
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.
npx create-react-app ama --use-npm
Install our dependencies
cd ama
npm install aws-amplify node-sass react-router react-router-dom @aws-amplify/ui-react
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.
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:
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!
}
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
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
}
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.
? 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
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! 🎉
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.
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
}
}
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?"
}
}
}
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.
createAnswer mutation
mutation createAnswer($questionID: ID!, $answerContent: String!) {
createAnswer(input:{
questionID: $questionID
content: $answerContent
}) {
id
content
}
}
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"
}
}
}
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
}
}
}
}
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"
}
}
]
}
}
}
deleteQuestion mutation
One last thing, we should be able to delete a question.
mutation deleteQuestion($questionID: ID!) {
deleteQuestion(input: {id: $questionID}) {
content
}
}
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": []
}
}
}
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:
Top comments (0)