Tutorial

GraphQL API with AWS and Use with React

Draft updated on Invalid Date
author

dabit3

GraphQL API with AWS and Use with React

This tutorial is out of date and no longer maintained.

Introduction

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

Building, managing, and deploying your own GraphQL server can be tedious and time-consuming, especially for developers new to the technology that may want to quickly get a real-world, secure, and 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 and authorization.

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, and secure GraphQL APIs.

We’ll go through:

  • how to create a new GraphQL API
  • how to interact with it in the AWS console
  • how to access the API from a React application

Getting Started

We’ll be using AWS Amplify to create the AppSync service and connect to it from our React application. AWS Amplify is a CLI and Client toolchain that allows developers to create AWS services from their front-end environment and 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.

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

Users will be able to sign up for an account, sign in to the account, create a restaurant, and 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:

  1. create-react-app restaurant-app

Or:

  1. npx create-react-app restaurant-app

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

  1. cd restaurant-app
  2. npm install aws-amplify

Amplify CLI

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

  1. 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

  1. amplify configure

If you’d like a video walkthrough of how to install and configure the AWS Amplify CLI, click here.

Now that the CLI is installed and configured, we can initialize a new Amplify project in our React application. From the inside of your React application, run the following command:

  1. amplify init

Here, you will be prompted with questions about your application configuration. You can choose your default text editor and the defaults for all of the other options.

Once the project has been initialized, we can go ahead and add an API and 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:

  1. amplify add api

Now, you’ll be prompted for the following:

  • Please select from one of the below mentioned services: GraphQL
  • Provide API name: restaurantapi (or whatever name you’d like)
  • 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

Update the schema to this and 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:

  1. amplify push

Now, you’ll be prompted for the following:

  • Do you want to generate code for your newly created GraphQL API? Y
  • 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: Y

Once the push is complete, the AWS AppSync service has 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 and automatically expand the schema into an additional schema that is typically necessary for a robust API, including queries, mutations, subscriptions, resolvers, and a data source (Amazon DynamoDB). Everything you need to get up and 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.

View the AWS AppSync console and observe the API that was just created. View the Cognito user pool that was just created.

Make sure that you are in the same region in which you created your resource by viewing the region in the top right corner of the AWS console.

Testing the API in the AWS Console

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

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

What we’d like to do next though is execute mutations and queries against the API. In the left-hand menu, click on Queries and 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 and then we’ll query for them using the following query:

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

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

You may be wondering where the ID comes from since all we did was pass in a name and 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 and 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 and 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 and 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 an initial state to hold the restaurants array
  4. Query for the restaurants array from our API and 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 and create the state and 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 and the query definition. We then store the data in the restaurants array after it has returned from the array and reset the state.

Now, the data is stored in the state and 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)' }
}

For the final gist of all of the code for this component, check out this link.

Now, we should be able to run the app and see the queried data rendered to our screen:

  1. npm start

GraphQL Mutations

Now that we know how to query for data, let’s look at how to submit mutations and 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 and display them to the screen.

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

  1. Import mutation definition
  2. Create an initial state to hold user input values for the restaurant name and description form data
  3. Create a method for handling the user input into the form
  4. Create a method to call the API and create the mutation
  5. Create a form to handle the methods

First, we’ll import the mutation:

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

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

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

Now, we’ll create the two methods we’ll need to handle user input and 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. We first check to make sure the form input values are not empty.
  2. We then create a new object called restaurant and store the name and description values in the object.
  3. Next, we create a new array called restaurants and set the value as a new array containing all previous restaurants in the state as well as the new restaurant object.
  4. We call setState to update the UI with the new restaurants array and reset the form fields.
  5. Finally, we make the API call, passing in the restaurant variable to the API.graphql graphqlOperation as a variable.

Finally, we’ll create the UI for the form fields and 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
  }
}

For a final gist for all of the code for this component, click here.

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 is 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, and 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 subscription definition
  2. Initialize the subscription in componentDidMount
  3. Remove the subscription in componentDidUnmount
// 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 andand 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. We create a variable to store the restaurant data coming through the subscription.
  2. We create a newly updated restaurants array, filtering to make sure there are no duplicate items in case our application is the app creating the mutation. (This implementation checks for duplicate names and descriptions. Ideally, you would have a client ID in which to filter on also stored in the database which would serve as a proper way to filter duplicate items).
  3. After the restaurants array is created we reset the state with the new restaurants array.

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 and 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, and 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), and the AWS AppSync JavaScript SDK (which we did not cover).

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

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products


About the authors
Default avatar
dabit3

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel