An introduction GraphQL with AWS AppSync

An introduction GraphQL with AWS AppSync

In this tutorial, we’ll learn how to take advantage of AWS AppSync, a managed GraphQL service, to build a full-stack application without writing a single line of back-end code.

In this tutorial, we’ll learn how to take advantage of AWS AppSync, a managed GraphQL service, to build a full-stack application without writing a single line of back-end code.

GraphQL is becoming increasingly popular. The problem is that if you are a front-end developer, you are only half of the way there. GraphQL is not just a client technology. The server also has to be implemented according to the specification. This means that in order to implement GraphQL into your application, you need to learn not only GraphQL on the front end, but also GraphQL best practices, server-side development, and everything that goes along with it on the back end.

There will come a time when you will also have to deal with issues like scaling your server, complex authorization scenarios, malicious queries, and more issues that require more expertise and even deeper knowledge around what is traditionally categorized as back-end development.

Thankfully, we have an array of managed back-end service providers today that allow front-end developers to only worry about implementing features on the front end without having to deal with all of the traditional back-end work.

Services like Firebase (API) / AWS AppSync (database), Cloudinary (media), Algolia (search) and Auth0 (authentication) allow us to offload our complex infrastructure to a third-party provider and instead focus on delivering value to end users in the form of new features instead.

In this tutorial, we’ll learn how to take advantage of AWS AppSync, a managed GraphQL service, to build a full-stack application without writing a single line of back-end code.

While the framework we’re working in is React, the concepts and API calls we will be using are framework-agnostic and will work the same in Angular, Vue, React Native, Ionic or any other JavaScript framework or application.

We will be building a restaurant review app. In this app, we will be able to create a restaurant, view restaurants, create a review for a restaurant, and view reviews for a restaurant.

The tools and frameworks that we will be using are React, AWS Amplify, and AWS AppSync.

AWS Amplify is a framework that allows us to create and connect to cloud services, like authentication, GraphQL APIs, and Lambda functions, among other things. AWS AppSync is a managed GraphQL service.

We’ll use Amplify to create and connect to an AppSync API, then write the client side React code to interact with the API.

View Repo

Getting started

The first thing we’ll do is create a React project and move into the new directory:

npx create-react-app ReactRestaurants

cd ReactRestaurants

Next, we’ll install the dependencies we’ll be using for this project. AWS Amplify is the JavaScript library we’ll be using to connect to the API, and we’ll use Glamor for styling.

yarn add aws-amplify glamor

The next thing we need to do to is install and configure the Amplify CLI:

npm install -g @aws-amplify/cli

amplify configure

Amplify’s configure will walk you through the steps needed to begin creating AWS services in your account. For a walkthrough of how to do this, check out this video.

Now that the app has been created and Amplify is ready to go, we can initialize a new Amplify project.

amplify init

Amplify init will walk you through the steps to initialize a new Amplify project. It will prompt you for your desired project name, environment name, and text editor of choice. The CLI will auto-detect your React environment and select smart defaults for the rest of the options.

Creating the GraphQL API

One we’ve initialized a new Amplify project, we can now add the Restaurant Review GraphQL API. To add a new service, we can run the amplify add command.

amplify add api

This will walk us through the following steps to help us set up the API:

? Please select from one of the below mentioned services GraphQL
? Provide API name bigeats
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? N
? Do you want a guided schema creation? Y
? What best describes your project: Single object with fields
? Do you want to edit the schema now? Y

The CLI should now open a basic schema in the text editor. This is going to be the schema for our GraphQL API.

Paste the following schema and save it.

// amplify/backend/api/bigeats/schema.graphql

type Restaurant @model {
  id: ID!
  city: String!
  name: String!
  numRatings: Int
  photo: String!
  reviews: [Review] @connection(name: "RestaurantReview")
}
type Review @model {
  rating: Int!
  text: String!
  createdAt: String
  restaurant: Restaurant! @connection(name: "RestaurantReview")
}

In this schema, we’re creating two main types: Restaurant and Review. Notice that we have @model and @connection directives in our schema.

These directives are part of the GraphQL Transform tool built into the Amplify CLI. GraphQL Transform will take a base schema decorated with directives and transform our code into a fully functional API that implements the base data model.

If we were spinning up our own GraphQL API, then we’d have to do all of this manually:

  1. Define the schema
  2. Define the operations against the schema (queries, mutations, and subscriptions)
  3. Create the data sources
  4. Write resolvers that map between the schema operations and the data sources.

With the @model directive, the GraphQL Transform tool will scaffold out all schema operations, resolvers, and data sources so all we have to do is define the base schema (step 1). The @connection directive will let us model relationships between the models and scaffold out the appropriate resolvers for the relationships.

In our schema, we use @connection to define a relationship between Restaurant and Reviews. This will create a unique identifier for the restaurant ID for the review in the final generated schema.

Now that we’ve created our base schema, we can create the API in our account.

amplify push
? Are you sure you want to continue? Yes
? 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
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes

Because we’re creating a GraphQL application, we typically would need to write all of our local GraphQL queries, mutations and subscriptions from scratch. Instead, the CLI will be inspecting our GraphQL schema and then generating all of the definitions for us and saving them locally for us to use.

After this is complete, the back end has been created and we can begin accessing it from our React application.

If you’d like to view your AppSync API in the AWS dashboard, visit https://console.aws.amazon.com/appsync and click on your API. From the dashboard you can view the schema, data sources, and resolvers. You can also perform queries and mutations using the built-in GraphQL editor.

Building the React client

Now that the API is created and we can begin querying for and creating data in our API. There will be three operations we will be using to interact with our API:

  1. Define the schema
  2. Define the operations against the schema (queries, mutations, and subscriptions)
  3. Create the data sources
  4. Write resolvers that map between the schema operations and the data sources.

Before we start building the app, let’s take a look at how these operations will look and work.

Interacting with the AppSync GraphQL API

When working with a GraphQL API, there are many GraphQL clients available.

We can use any GraphQL client we’d would like to interact with an AppSync GraphQL API, but there are two that are configured specifically to work most easily. These are the Amplify client (what we will use) and the AWS AppSync JS SDK (similar API to Apollo client).

The Amplify client is similar to the fetch API in that it is promise-based and easy to reason about. The Amplify client does not support offline out of the box. The AppSync SDK is more complex but does support offline out of the box.

To call the AppSync API with Amplify, we use the API category. Here’s an example of how to call a query:

import { API, graphqlOperation } from 'aws-amplify'
import * as queries from './graphql/queries'

const data = await API.graphql(graphqlOperation(queries.listRestaurants))

For a mutation, it is very similar. The only difference is we need to pass in a a second argument for the data we are sending in the mutation:

import { API, graphqlOperation } from 'aws-amplify'
import * as mutations from './graphql/mutations'

const restaurant = { name: "Babalu", city: "Jackson" }
const data = await API.graphql(graphqlOperation(
  mutations.createRestaurant,
  { input: restaurant }
))

We use the graphql method from the API category to call the operation, wrapping it in graphqlOperation, which parses GraphQL query strings into the standard GraphQL AST.

We’ll be using this API category for all of our GraphQL operation in the app.

Here is the repo containing the final code for this project.

Configuring the React app with Amplify

The first thing we need to do in our app is configure it to recognize our Amplify credentials. When we created our API, the CLI created a new file called aws-exports.js in our src folder.

This file is created and updated for us by the CLI as we create, update and delete services. This file is what we’ll be using to configure the React application to know about our services.

To configure the app, open up src/index.js and add the following code:

import Amplify from 'aws-amplify'
import config from './aws-exports'
Amplify.configure(config)

Next, we will create the files we will need for our components. In the src directory, create the following files:

  • Header.js
  • Restaurant.js
  • Review.js
  • CreateRestaurant.js
  • CreateReview.js

Creating the components

While the styles are referenced in the code snippets below, the style definitions have been omitted to make the snippets less verbose. For style definitions, see the final project repo.

Next, we’ll create the Header component by updating [src/Header.js](https://github.com/dabit3/bigeats/blob/master/src/Header.js "src/Header.js").

// src/Header.js

import React from 'react'
import { css } from 'glamor'
const Header = ({ showCreateRestaurant }) => (
  <div {...css(styles.header)}>
    <p {...css(styles.title)}>BigEats</p>
    <div {...css(styles.iconContainer)}>
      <p {...css(styles.icon)} onClick={showCreateRestaurant}>+</p>
    </div>
  </div>
)

export default Header

Now that our Header is created, we’ll update [src/App.js](https://github.com/dabit3/bigeats/blob/master/src/App.js "src/App.js"). This file will hold all of the interactions with the API, so it is pretty large. We’ll define the methods and pass them down as props to the components that will call them.

// src/App.js

import React, { Component } from 'react'
import { API, graphqlOperation } from 'aws-amplify'

import Header from './Header'
import Restaurants from './Restaurants'
import CreateRestaurant from './CreateRestaurant'
import CreateReview from './CreateReview'
import Reviews from './Reviews'
import * as queries from './graphql/queries'
import * as mutations from './graphql/mutations'

class App extends Component {
  state = {
    restaurants: [],
    selectedRestaurant: {},
    showCreateRestaurant: false,
    showCreateReview: false,
    showReviews: false
  }
  async componentDidMount() {
    try {
      const rdata = await API.graphql(graphqlOperation(queries.listRestaurants))
      const { data: { listRestaurants: { items }}} = rdata
      this.setState({ restaurants: items })
    } catch(err) {
      console.log('error: ', err)
    }
  }
  viewReviews = (r) => {
    this.setState({ showReviews: true, selectedRestaurant: r })
  }
  createRestaurant = async(restaurant) => {
    this.setState({
      restaurants: [...this.state.restaurants, restaurant]
    })
    try {
      await API.graphql(graphqlOperation(
        mutations.createRestaurant,
        {input: restaurant}
      ))
    } catch(err) {
      console.log('error creating restaurant: ', err)
    }
  }
  createReview = async(id, input) => {
    const restaurants = this.state.restaurants
    const index = restaurants.findIndex(r => r.id === id)
    restaurants[index].reviews.items.push(input)
    this.setState({ restaurants })
    await API.graphql(graphqlOperation(mutations.createReview, {input}))
  }
  closeModal = () => {
    this.setState({
      showCreateRestaurant: false,
      showCreateReview: false,
      showReviews: false,
      selectedRestaurant: {}
    })
  }
  showCreateRestaurant = () => {
    this.setState({ showCreateRestaurant: true })
  }
  showCreateReview = r => {
    this.setState({ selectedRestaurant: r, showCreateReview: true })
  }
  render() {
    return (
      <div>
        <Header showCreateRestaurant={this.showCreateRestaurant} />
        <Restaurants
          restaurants={this.state.restaurants}
          showCreateReview={this.showCreateReview}
          viewReviews={this.viewReviews}
        />
        {
          this.state.showCreateRestaurant && (
            <CreateRestaurant
              createRestaurant={this.createRestaurant}
              closeModal={this.closeModal}   
            />
          )
        }
        {
          this.state.showCreateReview && (
            <CreateReview
              createReview={this.createReview}
              closeModal={this.closeModal}   
              restaurant={this.state.selectedRestaurant}
            />
          )
        }
        {
          this.state.showReviews && (
            <Reviews
              selectedRestaurant={this.state.selectedRestaurant}
              closeModal={this.closeModal}
              restaurant={this.state.selectedRestaurant}
            />
          )
        }
      </div>
    );
  }
}
export default App

We first create some initial state to hold the restaurants array that we will be fetching from our API. We also create Booleans to control our UI and a selectedRestaurant object.

In componentDidMount, we query for the restaurants and update the state to hold the restaurants retrieved from the API.

In createRestaurant and createReview, we send mutations to the API. Also notice that we provide an optimistic update by updating the state immediately so that the UI gets updated before the response comes back in order to make our UI snappy.

Next, we’ll create the Restaurants component ([src/Restaurants.js](https://github.com/dabit3/bigeats/blob/master/src/Restaurants.js "src/Restaurants.js")).

// src/Restaurants.js

import React, { Component } from 'react';
import { css } from 'glamor'

class Restaurants extends Component {
  render() {
    const { restaurants, viewReviews } = this.props
    return (
      <div {...css(styles.container)}>
        {
          restaurants.length === Number(0) && (
            <h1
              {...css(styles.h1)}
            >Create your first restaurant by clicking +</h1>
          )
        }
        {
          restaurants.map((r, i) => (
            <div key={i}>
              <img
                src={r.photo}
                {...css(styles.image)}
              />
              <p {...css(styles.title)}>{r.name}</p>
              <p {...css(styles.subtitle)}>{r.city}</p>
              <p
                onClick={() => viewReviews(r)}
                {...css(styles.viewReviews)}
              >View Reviews</p>
              <p
                onClick={() => this.props.showCreateReview(r)}
                {...css(styles.createReview)}
              >Create Review</p>
            </div>
          ))
        }
      </div>
    );
  }
}

export default Restaurants

This component is the main view of the app. We map over the list of restaurants and show the restaurant image, its name and location, and links that will open overlays to show reviews and create a new review.

Next, we’ll look at the Reviews component ([src/Reviews.js](https://github.com/dabit3/bigeats/blob/master/src/Reviews.js "src/Reviews.js")). In this component, we map over the list of reviews for the chosen restaurant.

// src/Reviews.js

import React from 'react'
import { css } from 'glamor'

class Reviews extends React.Component {
  render() {
    const { closeModal, restaurant } = this.props
    return (
      <div {...css(styles.overlay)}>
        <div {...css(styles.container)}>
          <h1>{restaurant.name}</h1>
          {
            restaurant.reviews.items.map((r, i) => (
              <div {...css(styles.review)} key={i}>
                <p {...css(styles.text)}>{r.text}</p>
                <p {...css(styles.rating)}>Stars: {r.rating}</p>
              </div>
            ))
          }
          <p onClick={closeModal}>Close</p>
        </div>
      </div>
    )
  }
}

export default Reviews

Next, we’ll take a look at the CreateRestaurant component ([src/CreateRestaurant.js](https://github.com/dabit3/bigeats/blob/master/src/CreateRestaurant.js "src/CreateRestaurant.js")). This component holds a form that keeps up with the form state. The createRestaurant class method will call this.props.createRestaurant, passing in the form state.

// src/CreateRestaurant.js

import React from 'react'
import { css } from 'glamor';

class CreateRestaurant extends React.Component {
  state = {
    name: '', city: '', photo: ''
  }
  createRestaurant = () => {
    if (
      this.state.city === '' || this.state.name === '' || this.state.photo === ''
    ) return
    this.props.createRestaurant(this.state)
    this.props.closeModal()
  }
  onChange = ({ target }) => {
    this.setState({ [target.name]: target.value })
  }
  render() {
    const { closeModal } = this.props
    return (
      <div {...css(styles.overlay)}>
        <div {...css(styles.form)}>
          <input
            placeholder='Restaurant name'
            {...css(styles.input)}
            name='name'
            onChange={this.onChange}
          />
          <input
            placeholder='City'
            {...css(styles.input)}
            name='city'
            onChange={this.onChange}
          />
          <input
            placeholder='Photo'
            {...css(styles.input)}
            name='photo'
            onChange={this.onChange}
          />
          <div
            onClick={this.createRestaurant}
            {...css(styles.button)}
          >
            <p
              {...css(styles.buttonText)}
            >Submit</p>
          </div>
          <div
            {...css([styles.button, { backgroundColor: '#555'}])}
            onClick={closeModal}
          >
            <p
              {...css(styles.buttonText)}
            >Cancel</p>
          </div>
        </div>
      </div>
    )
  }
}

export default CreateRestaurant

Next, we’ll take a look at the CreateReview component ([src/CreateReview.js](https://github.com/dabit3/bigeats/blob/master/src/CreateReview.js "src/CreateReview.js")). This component holds a form that keeps up with the form state. The createReview class method will call this.props.createReview, passing in the restaurant ID and the form state.

// src/CreateReview.js

import React from 'react'
import { css } from 'glamor';
const stars = [1, 2, 3, 4, 5]
class CreateReview extends React.Component {
  state = {
    review: '', selectedIndex: null
  }
  onChange = ({ target }) => {
    this.setState({ [target.name]: target.value })
  }
  createReview = async() => {
    const { restaurant } = this.props
    const input = {
      text: this.state.review,
      rating: this.state.selectedIndex + 1,
      reviewRestaurantId: restaurant.id
    }
    try {
      this.props.createReview(restaurant.id, input)
      this.props.closeModal()
    } catch(err) {
      console.log('error creating restaurant: ', err)
    }
  }
  render() {
    const { selectedIndex } = this.state
    const { closeModal } = this.props
    return (
      <div {...css(styles.overlay)}>
        <div {...css(styles.form)}>
          <div {...css(styles.stars)}>
            {
              stars.map((s, i) => (
                <p
                  key={i}
                  onClick={() => this.setState({ selectedIndex: i })}
                  {...css([styles.star, selectedIndex === i && { backgroundColor: 'gold' }])}
                >{s} star</p>
              ))
            }
          </div>
          <input
            placeholder='Review'
            {...css(styles.input)}
            name='review'
            onChange={this.onChange}
          />
          <div
            onClick={this.createReview}
            {...css(styles.button)}
          >
            <p
              {...css(styles.buttonText)}
            >Submit</p>
          </div>
          <div
            {...css([styles.button, { backgroundColor: '#555'}])}
            onClick={closeModal}
          >
            <p
              {...css(styles.buttonText)}
            >Cancel</p>
          </div>
        </div>
      </div>
    )
  }
}

export default CreateReview

Running the app

Now that we have built our back-end, configured the app and created our components, we’re ready to test it out:

npm start

Now, navigate to [http://localhost:3000](http://localhost:3000 "http://localhost:3000"). Congratulations, you’ve just built a full-stack serverless GraphQL application!

Conclusion

The next logical step for many applications is to apply additional security features, like authentication, authorization and fine-grained access control. All of these things are baked into the service. To learn more about AWS AppSync security, check out the documentation.

If you’d like to add hosting and a Continuous Integration/Continuous Deployment pipeline for your app, check out the Amplify Console.

I also maintain a couple of repositories with additional resources around Amplify and AppSync: Awesome AWS Amplify and Awesome AWS AppSync.

Building Robust GraphQL APIs with AWS Amplify and AWS AppSync

Building Robust GraphQL APIs with AWS Amplify and AWS AppSync

GraphQL is a solid way of building responsive web and mobile APIs. It's not always obvious, however, how to structure your APIs. When should you use a transformer? When are GraphQL subscriptions appropriate? In this tech talk, we'll go through the principles of building robust APIs powered by GraphQL for a range of interesting use cases.

GraphQL is a solid way of building responsive web and mobile APIs. It's not always obvious, however, how to structure your APIs. When should you use a transformer? When are GraphQL subscriptions appropriate? In this tech talk, we'll go through the principles of building robust APIs powered by GraphQL for a range of interesting use cases.

Learning Objectives:

  • Learn about the main principles that define robust GraphQL APIs
  • Learn about the building blocks that are available and when to use them
  • See how AWS Amplify and AWS AppSync make it easy to run your GraphQL backend

AWS Training Online Course - Best AWS Certification - Intellipaat

AWS Training Online Course - Best AWS Certification - Intellipaat

Enroll for best AWS training course with AWS Certification online and placement support. Learn Amazon web services AWS course from certified AWS experts. Enroll Now!

Enroll for best AWS training course with AWS Certification online and placement support. Learn Amazon web services AWS course from certified AWS experts. Enroll Now!

GraphQL API with AWS and Use with React

GraphQL API with AWS and Use with React

Create a serverless GraphQL API with AWS and consume it with React.

GraphQL has become a go-to API implementation for developers looking to take advantage of features liketype safety, network efficiency, real-time data with subscriptions & more.

Building, managing, & deploying your own GraphQL server can be tedious & time consuming, especially for developers new to the technology that may want to quickly get a real-world, secure & scalable GraphQL API up and running without having to learn how to create it from scratch (as well as spend the time learning best practices around API design & authorization).

Introducing AWS AppSync

Table of Contents

In this post we’ll learn how to use AWS AppSync, a fully managed GraphQL service, to create a serverless GraphQL API. AWS AppSync allows developers to quickly create serverless, scalable & secure GraphQL APIs.

We’ll go through:

Our CLI Tool: AWS Amplify

We’ll be using AWS Amplify to create the AppSync service & connect to it from our React application. AWS Amplify is a CLI & Client toolchain that allows developers to create AWS services from their front-end environment & connect to them from the client-side applications.

The app that we will be building is a restaurant tracker app that will allow us to keep up with all of the restaurants we would like to visit as well as store information about each restaurant. We’ll use React as the front-end for the application.

Getting Started

The AWS Amplify CLI is the tool that we will be using to create the AWS AppSync API. This CLI allows us to create & deploy AWS services directly from the command line. The services that we will be creating are AWS AppSync for the API layer & Amazon Cognito for the authentication provider.

App Features

Users will be able to sign up for an account, sign in to the account, create a restaurant, & query for only the restaurants that the user created.

Creating the React App

First, we’ll create the React application that we’ll be using to connect to our API. To do so, we’ll use Create React App:

create-react-app restaurant-app

# or
npx create-react-app restaurant-app

Once the app is created, we’ll change into the new directory & install aws-amplify:

cd restaurant-app
npm install aws-amplify

Amplify CLI

The next thing we need to do is install & configure the AWS Amplify CLI. To do so, we’ll use npm:

npm install -g @aws-amplify/cli

Once the CLI is installed, we’ll need to configure it to use an IAM user from our AWS account

amplify configure

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:

amplify init

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:
Once the project has been initialized, we can go ahead and add an an API & authentication to our app. To do so, we’ll first add authentication.

Adding the AWS AppSync API

To add the AWS AppSync api, we can run the following command:

amplify add api

Now, you’ll be prompted for the following:

Update the schema to this & save the file:

type Restaurant @model {
  id: ID!
  name: String!
  description: String
}

Next, we’ll run the push command to create the services in our account:

amplify push

Now, you’ll be prompted for the following:

Once the push is complete, the AWS AppSync service have been successfully created in our account.

What exactly has been created? If you notice, in the above schema definition, we have an @model directive on the schema type definition. This is part of the Amplify GraphQL Transform library.

The library will recognize this directive & automcatically expand the schema into additional schema that is typically necessary for a robust API, including queries, mutations, subscriptions, resolvers & a data source (Amazon DynamoDB). Everything you need to get up & running is now set up for you using this directive.

If at any time you would like to view the services that have been created in your Amplify configuration, you can run amplify status.

To view the AWS AppSync console & see the API that was just created, click here. To view the Cognito user pool that was just created, click here.

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:## Testing the API in the AWS Console

Now that the API has been created, we can go to the AWS AppSync console & begin testing it out! To do so, visit the AWS AppSync console (be sure you are in the correct region), & click on the API name you just created.

In the left hand menu, you will see links for your data source, settings, schema, & queries. Feel free to click around here & explore.

What we’d like to do next though is execute mutations & queries against the API. In the left hand menu, click on Queries & try out the following mutation in the query editor:

mutation create {
  createRestaurant(input: {
    name: "Soi"
    description: "Great Thai in Seattle"
  }) {
    id name d
  }
}

Creating a query in the AWS Console

Create a couple of mutations & then we’ll query for them using the following query:

query list {
  listRestaurants {
    items {
      id
      name
      description
    }
  }
}

This query should return an array of restaurans showing the id, name & description for each item created.

You may be wondering where the ID comes from, since all we did was pass in a name & description. The ids are automatically generated by the service (in the resolver mapping template) so you don’t have to pass them in. If you’d like to view the mapping template for any resolver, click on Schema in the left menu, look for the resolver you’d like to view in the resolvers panel on the right.

Integration with React

Now that the services are created, we can wire them up with our React application & begin interacting with them.

The first thing we’ll do is configure the application with our Amplify resources. To configure the React application, open index.js & add the following three lines of code:

import Amplify from 'aws-amplify'
import config from './aws-exports'
Amplify.configure(config)

Next, open App.js. Here, we’ll query for the restaurants & display them in our app. To do so, we’ll need to do 5 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.

First, we’ll take care of our imports:

import { API, graphqlOperation } from 'aws-amplify'
import { listRestaurants } from './graphql/queries'

Next, in the class definition, we’ll go ahead & create the state & initialize the query in componentDidMount lifecycle method:

state = { restaurants: [] }
async componentDidMount() {
  try {
    const apiData = await API.graphql(graphqlOperation(listRestaurants))
    const restaurants = apiData.data.listRestaurants.items
    this.setState({ restaurants })
  } catch (err) {
    console.log('error: ', err)
  }
}

In componentDidMount we fetch the data from the AppSync API by using the API class, calling API.graphql passing in the graphqlOperation helper & the query definition. We the store the data in the restaurants array after it has returned from the array & reset the state.

Now, the data is stored in the state & we can render it to our screen. Let’s update the render method to show the restaurant data in our UI:

// rest of class omitted
render() {
    return (
      <div className="App">
        {
          this.state.restaurants.map((rest, i) => (
            <div style={styles.item}>
              <p style={styles.name}>{rest.name}</p>
              <p style={styles.description}>{rest.description}</p>
            </div>
          ))
        }
      </div>
    );
  }
}

const styles = {
  item: {
    padding: 10,
    borderBottom: '2px solid #ddd'
  },
  name: { fontSize: 22 },
  description: { color: 'rgba(0, 0, 0, .45)' }
}

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:
Now, we should be able to run the app & see the queried data rendered to our screen:

npm start

GraphQL Mutations

Now that we know how to query for data, let’s look at how to submit mutations & create new items in our API. To do so, we’ll create a form with an input that will create new items in our API & display them to the screen.

To do so, we’ll need to do 5 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.

First, we’ll import the mutation:

import { createRestaurant } from './graphql/mutations'

Next, we’ll update the state to hold the restaurant name & description:

 state = { name: '', description: '', restaurants: [] }

Now, we’ll create the two methods we’ll need to handle user input & call the API to create a restaurant:

onChange = e => {
  this.setState({ [e.target.name]: e.target.value })
}
createRestaurant = async () => {
  const { name, description } = this.state
  if (name === '' || description === '') return
  try {
    const restaurant = { name, description }
    const restaurants = [...this.state.restaurants, restaurant]
    this.setState({ restaurants, name: '', description: '' })
    await API.graphql(graphqlOperation(createRestaurant, {input: restaurant}))
    console.log('restaurant successfully created!')
  } catch (err) {
    console.log('error: ', err)
  }
}

In the createRestaurant method we do 5 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.

Finally, we’ll create the UI for the form fields & button to call the createRestaurant method:

<div style={styles.inputContainer}>
  <input
    name='name'
    placeholder='restaurant name'
    onChange={this.onChange}
    value={this.state.name}
    style={styles.input}
  />
  <input
    name='description'
    placeholder='restaurant description'
    onChange={this.onChange}
    value={this.state.description}
    style={styles.input}
  />
</div>
<button
  style={styles.button}
  onClick={this.createRestaurant}
>Create Restaurant</button>

const styles = {
  // existing styles omitted
  inputContainer: {
    margin: '0 auto', display: 'flex', flexDirection: 'column', width: 300
  },
  button: {
    border: 'none', backgroundColor: '#ddd', padding: '10px 30px'
  },
  input: {
    fontSize: 18,
    border: 'none',
    margin: 10,
    height: 35,
    backgroundColor: "#ddd",
    padding: 8
  }
}

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:
Now, we should be able to run the app and create new restaurants from the React application:

GraphQL Subscriptions

As I mentioned in the introduction, one of the most powerful features of GraphQL are real-time data with subscriptions.

Subscriptions push data from the server to the clients that choose to listen to the real time data coming from the server. We can subscribe to the creation of a new item, in our case the creation of a new restaurant, & get notified of the creation of the item as well as receive the item data in the subscription callback.

To implement subscriptions, we need to do three things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.
// import the subscription defition
import { onCreateRestaurant } from './graphql/subscriptions'

// initialize the subscription in componentDidMount
async componentDidMount() {
  // other code from this method omitted
  this.subscription = API.graphql(
    graphqlOperation(onCreateRestaurant)
  ).subscribe({
    next: restaurantData => {
      const restaurant = restaurantData.value.data.onCreateRestaurant
      const restaurants = [
        ...this.state.restaurants.filter(r => {
          return (
            r.name !== restaurant.name && r.description !== restaurant.description
          )
        }),
        restaurant
      ]
      this.setState({ restaurants })
    }
  })
}

// remove the subscription in componentWillUnmount
componentWillUnmount() {
  this.subscription.unsubscribe()
}

When the subscription fires, we do 3 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.
Conclusion

In this tutorial we’ve walked through all of the typical GraphQL operations you would use in a real-world application. We’ve also built & deployed a real-world GraphQL API that will automatically scale for you as your application scales.

The only data source we’ve looked at so far is DynamoDB, but AWS AppSync also supports other data sources out of the box (including Amazon ElasticSearch, AWS Lambda, & http resolvers). If you’re interested in learning more about using other data sources, check out the documentation here.

AWS AppSync has 2 JavaScript clients: AWS Amplify (which we have already covered), & the AWS AppSync JavaScript SDK (which we did not cover).

If you are looking for a solution that handles offline data & operations for you, then take a look at the AWS AppSync JavaScript SDK because it’s built in & works out of the box. You can still use Amplify to create & configure the AWS AppSync service while using the AWS AppSync JS SDK to interact with the service.