The Talent500 Blog
How to Build Serverless GraphQL and REST APIs Using AWS Amplify 1

How to Build Serverless GraphQL and REST APIs Using AWS Amplify

Introduction

AWS Amplify is a client library, command-line interface toolchain, and UI component library that enables developers to easily construct and connect to powerful cloud services. We’ll explore how to construct fully serverless web apps using React and AWS Amplify, including authentication, a managed GraphQL data layer, storage, lambda functions, and web hosting, in this article.

Amplify supports managed GraphQL (AWS AppSync), storage (Amazon S3), user authentication (Amazon Cognito), serverless functions (AWS Lambda), hosting (Amazon CloudFront and Amazon S3), analytics (Amazon Pinpoint), and other capabilities.

In this blog, we’ll take React and AWS Amplify a step further and look at features like a managed GraphQL data layer and lambda functions.

Adding a GraphQL API

Let’s have a look at how to add an AWS AppSync GraphQL API to our project and start utilising it.

The API we will design will be a restaurant API that will allow us to keep track of restaurants that we enjoy or want to visit.

We can use the following command to add the GraphQL API to our project:

amplify add api

Some setup questions will be posed to you. Select one of the following alternatives:

  • service type: GraphQL
  • API name: TutsGraphQLAPI
  • authorization type: API key
  • annotated GraphQL schema: N
  • guided schema creation: Y
  • What best describes your project? Single object with fields (e.g. “Todo” with ID, name, description)
  • Do you want to edit the schema now? Y

When asked, edit the schema as follows and save the file:

1.// located at amplify-web-app/amplify/backend/api/TutsGraphQLAPI/schema.graphql
2.
3. type Restaurant @model {
4  id: ID!
5. name: String!
6. description: String
7. }


This simply defines a single data type—Restaurant—with necessary id, name, and optional description attributes.

Let’s now push the adjustments to our account:

amplify push

The API has now been developed!

What exactly occurred here? AWS Amplify created a full GraphQL API using the built-in GraphQL Transform library, which included extra schema, resolvers, and a data source.

To view the newly formed AWS AppSync API, go to the dashboard at https://console.aws.amazon.com/appsync and click on the API that was just established (be sure that your region is set correctly). You may check the API setup and conduct queries and changes on the API from the AWS AppSync dashboard.

Execute GraphQL Mutations

Let’s now use the React Application to connect with the API.

The first thing we want to do is make a mutation. Mutations in GraphQL are comparable to REST’s PUT, PUSH, and DELETE operations. We’ll develop a mutation to produce a new restaurant item because we don’t currently have any data in our database.

To do this, we will import API and graphqlOperation from AWS Amplify, define a mutation, and then execute the mutation.

Let’s take a look at an example program that uses a mutation. First, we import React, our app CSS, and the appropriate AWS Amplify components into App.js.

  1. import React, { Component } from ‘react’;
    2.  import ‘./App.css’;
    3.
    4.  import { withAuthenticator } from ‘aws-amplify-react’
    5.  import { API, graphqlOperation } from ‘aws-amplify’

Then, in order to establish a restaurant, we specify a mutation. The mutation is named createRestaurant and accepts a name and description. When we constructed the Restaurant schema above, we automatically defined this modification. It’s worth noting that the mutation is written in GraphQL, a domain-specific query language.

  1. const CreateRestaurant = `
    2. mutation($name: String!, $description: String) {
    3. createRestaurant(input: {
    4. name: $name
    5. description: $description
    6. }) {
    7. id name description
    8. }
    9. }
    10

We will create the app component.

  1. class App extends Component {
    2. //create initial state
    3. state = {name: ”, description: ”}
    4.
    5. //update state when user types into inputs
    6. onChange = e => {
    7. this.setState({ [e.target.name]: e.target.value })
    8. }
    9.
    10. //define function to execute mutation
    11. //render the component
    12. }

We then construct a function to execute the modification within the App component. This calls API.graphql and passes in the mutation and data to execute the mutation.

1. //define function to execute mutation
2. createRestaurant = async() => {
3. if (this.state.name === ” || this.state.description === ”) return
4. try {
5. const restaurant = { name: this.state.name, description: this.state.description }
6. await API.graphql(graphqlOperation(CreateRestaurant, restaurant))
7. this.setState({ name: ”, description: ” })
8. console.log(‘restaurant successfully created!’)
9. } catch (err) {
10. console.log(‘error creating restaurant…’)
11. }
12. }


Now render the component to link up change handler and mutation functions.

  1. //render the component
    2. render() {
    3. return (
    4. <div className=”App”>
    5. <input value={this.state.name} onChange={this.onChange} name=’name’ />
    6. <input value={this.state.description} onChange={this.onChange} name=’description’ />
    7. <button onClick={this.createRestaurant}>Create Restaurant</button>
    8. </div>
    9. )
    10. }


    Finally, export the App component, with authentication.
  2. export default withAuthenticator(App, { includeGreetings: true });

You should be able to execute this code and use the API to create new restaurant products.

To see if the data is present, enter the AWS AppSync dashboard, choose your API, click on Data Sources in the left menu, and then click on the Resource Name. The Amazon DynamoDB table will be opened. The data under the Items tab may be viewed in the table.

Running GraphQL Queries

Let’s now look at how to query the API for data. We’ll do this in three steps:

  1. define a query
  2. execute the query when the app loads
  3. save the result from the query in our state and render it in the UI

First, create a new component to define the query. To specify the query, we’re once again utilising the GraphQL language. We’re employing the listRestaurants query, which was created automatically when we published the Restaurants schema. The following excerpt says that we anticipate a list of objects, each having an id, name, and description.

  1. const ListRestaurants = `
    2. query {
    3. listRestaurants {
    4. items {
    5. id name description
    6. }
    7. }
    8. }

Following that, we’ll need to add some more initial state to retain the array of restaurants returned by the server.

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

To query data from the GraphQL server, we’ll also need to add a componentDidMount lifecycle event. When the restaurant list is received from the server, this async function will update the component state.

1. async componentDidMount() {
2. try {
3. const restaurants = await API.graphql(graphqlOperation(ListRestaurants))
4. console.log(‘restaurants: ‘, restaurants)
5. this.setState({ restaurants: restaurants.data.listRestaurants.items })
6. } catch (err) {
7. console.log(‘error fetching data: ‘, err)
8. }
9. }

Finally, we’ll make a component that converts the restaurants array from component state to HTML.

  1. {
    2. this.state.restaurants.map((r, i) => (
    3. <div key={i}>
    4. <p>{r.name}</p>
    5. <p>{r.description}</p>
    6. </div>
    7. ))
    8. }

When we launch the app, we’ll see that the API data is shown in a list on the screen. However, when the data is updated, such as when you add a new restaurant, the app will not display any changes.

To begin, let’s modify the createRestaurant function to return an optimistic response to the UI. Currently, when we add a new item, the database is updated, but the UI is unaware of the new item. To correct this, we’ll add the following new item to the restaurant array in the createRestaurant method:

createRestaurant = async() => {
2. if (this.state.name === ” || this.state.description === ”) return
3. try {
4. const restaurant = { name: this.state.name, description: this.state.description }
5. const restaurants = […this.state.restaurants, restaurant]
6. this.setState({ name: ”, description: ”, restaurants })
7. await API.graphql(graphqlOperation(CreateRestaurant, restaurant))
8. console.log(‘restaurant successfully created!’)
9. } catch (err) {
10. console.log(‘error creating restaurant…’)
11. }
12. }

Real-Time Data Subscriptions

Then we’d like to be able to deal with real-time data. GraphQL subscriptions allow you to listen for data in real time. When fresh data becomes available, the subscription is activated, and the new data is delivered down through the subscription. It is our responsibility as clients to handle this new data.

We will subscribe to the array of restaurants in our app, and we will construct a onCreateRestaurant subscription that will trigger whenever a new restaurant is created. Then we’ll take the new subscription item, update our existing array, and execute setState to re-render the UI with the new data.

We begin by defining the subscription in the GraphQL domain-specific language, just as we did with mutations and queries.

  1. // define the subscription
    2. const OnCreateRestaurant = `
    3. subscription {
    4. onCreateRestaurant {
    5. id name description
    6. }
    7. }
    8

The subscription will be generated in the componentDidMount lifecycle action either before or after the existing GraphQL query:

  1. async componentDidMount() {
    2. try {
    3. const restaurants = await API.graphql(graphqlOperation(ListRestaurants))
    4. console.log(‘restaurants: ‘, restaurants)
    5. this.setState({ restaurants: restaurants.data.listRestaurants.items })
    6. } catch (err) {
    7. console.log(‘error fetching data: ‘, err)
    8. }
    9. API.graphql(graphqlOperation(OnCreateRestaurant))
    10. .subscribe({
    11. next: eventData => {
    12. const data = eventData.value.data.onCreateRestaurant
    13. console.log(‘data: ‘, data)
    14. const restaurants = [
    15. …this.state.restaurants.filter(r => r.name !== data.name && r.description !== data.description),
    16. Data
    17. ]
    18. this.setState({ restaurants })
    19. }
    20. })
    21. }


    Now, if you open two browser windows, you should be able to make a mutation in one and have the update take effect on the other screens.

If you look at the.filter function we used to create the new restaurants array in the subscription, you’ll notice we’re looking for duplicates that have both the same name and description. Perhaps a better method to achieve this in production would be to generate a unique client ID that is also saved in the database, and then filter on that identification.

Creating a REST API With AWS Lambda

Although GraphQL is a great cutting-edge technology, our project may need the development of a classic REST API. It’s also simple to use the CLI to develop serverless REST APIs using AWS Lambda and Amplify.

We used the amplify create api command to construct the GraphQL API. This command allows us to create either a GraphQL API or a REST API. The REST API may be set to utilise a solo serverless Express function or a serverless JavaScript function preconfigured to interact with Amazon DynamoDB CRUD operations.

We will be utilising a serverless Express function for this API.

Let us proceed with the new feature:

amplify add api

As is customary, this will request you to provide some setup information. Provide the following options:

  • The service type: REST
  • Mention a resource name that will be used within the project: e.g. amplifyrestapi
  • enter a path for the REST endpoints: e.g. /people
  • Lambda source: Create a new Lambda function
  • AWS Lambda function name: amplifyrestapifunction
  • The function template: Serverless express function (Integration with Amazon API Gateway)
  • edit the local lambda function now? Y

You may now modify the lambda function locally. We’ll replace the present app.get(‘/people’) function in the code with the following:

  1. // amplify-web-app/amplify/backend/function/amplifyrestapi/src/app.js
    2.
    3. app.get(‘/people’, function(req, res) {
    4. const people = [
    5. { name: “Nader” }, { name: “Amanda” }, { name: “Chris” }, { name: “” }
    6. ]
    7. res.json({
    8. success: true,
    9. People
    10. })
    11. });

For demonstration purposes, this just produces a constant list of names. Save this file and proceed with the following responses:

  • restrict API access? Yes
  • Who should have access? Authenticated users only
  • What kind of access is wanted for Authenticated users? read
  • add another path? N

This has established a new Lambda function on our local machine that we can change and upload to our account as required. This lambda function’s code may be found at

amplify/backend/function/amplifyrestapi/src.

Let’s now push the adjustments to our account:

amplify push

Querying the REST API From the Client

Our Lambda function is now operational, and we can begin interacting with it!

First, let’s get data from the new API and display it in our UI. To accomplish so, we’ll use Amplify’s API class and use API.get. In the last section, we used API.graphql to send queries to our GraphQL API, but the API class contains many more methods.

1. import { API } from ‘aws-amplify’
2.
3. // in the initial state, create an empty array of people
4. state = { people: [] }
5.
6. // 2. in componentDidMount, we will fetch this data using the API class
7. try {
8. const peopleData = await API.get(‘amplifyrestapi’, ‘/people’)
9. this.setState({ people: peopleData.people })
10. } catch (err) {
11. console.log(‘error fetching from Lambda API’)
12. }
13.
14. // 3. render the people data to the UI in the render method
15. {
16. this.state.people.map((person, index) => (
17. <p key={index}>{person.name}</p>
18. ))
19. }

We should now be able to start the app, retrieve the persons data from our API, and display it on the screen.

Updating a Lambda Function From the CLI

We may update our Lambda function via the CLI in addition to building a new one.

Instead of hardcoding constants, let’s alter the method to call an API and retrieve data. To do this, we’ll make HTTP queries using the axios package and retrieve data from the Star Wars API.

To utilise axios, go to amplify/backend/function/amplifyrestapi/src and instal it there. Because Axios will be executing in the Lambda function server-side, it is installed in the Lambda function’s project folder rather than the main app folder.

  1. yarn add axios
    2.
    3. # or
    4.
    5. npm install axios

After installing axios, we’ll edit the Lambda function to retrieve data from the Star Wars API:

  1. var axios = require(‘axios’)
    2.
    3. app.get(‘/people’, function(req, res) {
    4. axios.get(‘https://swapi.co/api/people/‘)
    5. .then(response => {
    6. res.json({
    7. success: true,
    8. people: response.data.results
    9. })
    10. })
    11.catch(error => {
    12. res.json({
    13. success: false,
    14. Error
    15. })
    16. })
    17. });


    To update your Lambda function in the cloud, save the file and execute amplify push from the main project folder:

      amplify push

Our API has been updated and is now operational!

We should now see the data received from the Star Wars API when we reload the app.

Conclusion

Now you have learned how to get started with AWS Amplify and add it to your React project, as well as how to add authentication, storage, hosting, and a GraphQL or REST API without having to manually write or provision a server in this course. That is a lot of authority for app creators.

We hope these blog have sparked your interest in developing your own serverless web apps with serverless technologies with AWS Amplify!

0
Afreen Khalfe

Afreen Khalfe

A professional writer and graphic design expert. She loves writing about technology trends, web development, coding, and much more. A strong lady who loves to sit around nature and hear nature’s sound.

Add comment