Barebones AWS Amplify + React + GraphQL App
Tags: react, aws, graphql, javascript
Published: 2019-11-17 17:51:57

This is a simple guide on how to get a very basic React app going with GraphQL and AWS Amplify. Unfortunately, the AWS docs aren't the best and I find myself forgetting what to do and unable to follow their examples every time.

I think the biggest culprit is the part of the docs which cover adding Auth to your app. If you don't need Auth, you'll be tempted to skip that section, but unfortunately one of the most crucial steps is documented there and only there, instead of as a basic preliminary step for all apps that want to use Amplify.

Not only that, but all the tutorials and examples that I can find out there are super complex, including libraries and code design strategies that further confuse me and throw me off track. I am putting out the super barebones example that I wish I could find.

Finally, create-react-app sets you up with a functional component by default, whereas the docs use examples for a classical component, which is also confusing. So here goes:

Requirements:
(As of this post in mid November 2019)

* node 13.1.0
* amplify 3.16.0
* create-react-app 3.2.0

*This post assumes you already ran `aws configure` - this portion of the docs has never been an issue for me personally*

Steps:

```bash
$ create-react-app my-project-name
$ cd my-project-name
$ yarn add aws-amplify aws-amplify-react

$ amplify init
  # follow the default instructions
  # wait for the cloud processes to finish

$ amplify add api 
  # follow prompts for GraphQL + 
  # API Key for authorization +
  # default prompts that will guide you 
  # in creating a schema +
  # Single-Object +
  # No to editing the schema

# You now have a basic API for a simple ToDo model

$ amplify push
  # say Yes to generate the code for your GraphQL API
  # (all the default answers will work fine for this post)
  # ... and wait a while (-:
```

Now here is the crucial code that they sort of hide in the "Adding Auth" section of the docs, for those of us who do not need to add Auth:

Add this code to `index.js`

```js
// other imports...
import Amplify from "aws-amplify"
import awsconfig from "./aws-exports"

Amplify.configure(awsconfig)

// ReactDOM.render...
```

Next, start with a bare-bones `App.js`

```jsx
import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
    </div>
  );
}

export default App;
```

And so that our CSS isn't wonky later, consider replacing the `.App` definition in `App.css`:

```js
.App {
  padding: 1rem;

  /* instead of text-align: center; */
}
```

Now let's add a basic input to create a ToDo and store it at the AWS AppSync GraphQL backend

First, we will need to add a controlled input, and in a functional component, we must import `useState`

```js
// change FROM this:
import React from 'react';
// TO this:
import React, { useState } from 'react';
```

In the top of the App function, add this:

```js
function App() {
  const [todoName, setTodoName] = useState('')

  // ...the rest...
}
```

And return the JSX for the input + button:

```jsx
// ...

  return (
    <div className="App">
      <input type="text" value={todoName} onChange={handleChange} />
      <button onClick={addTodo}>Add ToDo</button>
    </div>
  );

//...
```

And the function to handle the controlled input change:

```js
// ...
  const handleChange = (evt) => {
    setTodoName(evt.target.value)
  }

// return ( ...
```

Now to enable the `addTodo` function we will import the API and graphQL tools:

```js
import { API, graphqlOperation } from "aws-amplify"
import { createTodo } from "./graphql/mutations
```

And finally the `addTodo` function

```js
// ...
  const addTodo = async () => {
    await API.graphql(
      graphqlOperation(createTodo, { input: { name: todoName } })
    )
    setTodoName('') // make the input blank again
  }
  // Another fun fact of the AWS docs is
  // they don't give the updated 
  // example that the 2nd argument 
  // is an object with the "input" property (-:

// ...
```

With your local server started via `yarn start` you ought to now be able to successfully create a ToDo in your AWS GraphQL backend!

Let's list our ToDos just to be sure.

_Note: the following is probably a very naïve implementation and others will suggest a "better" way but I just want to be sure that my application is working during this prototyping phase._

We'll import our GraphQL query:

```js
import { listTodos } from "./graphql/queries"
```

And add another useState to get an array of our ToDo items:

```js
const [todoItems, setTodoItems] = useState([])

```

Create a function to populate our ToDos from AWS:

```js
  const updateTodos = async () => {
    const allTodos = await API.graphql(graphqlOperation(listTodos))
    setTodoItems(allTodos.data.listTodos.items)
  }
```

Add some quick JSX to list our ToDos:

```jsx
// ...

return (
  // ...

  <ul>
    {todoItems.map((item) => {
      return <li key={item.id}>{ item.name }</li>
    })}
  </ul>
)
```

Modify our addTodo function:

_(yes there is a better way with graphQL subscriptions, but this is okay for today!)_


```js
const addTodo = async () => {
  await API.graphql(
    graphqlOperation(createTodo, { input: { name: todoName } })
  )
  setTodoName('')

  updateTodos() // here it is
}
```

And the very, very naïve call to `updateTodos` before returning our JSX:

```js
  // ...
 
  updateTodos()

  return ( //...
    //...
  )
}
```

Welp, that should be it!

Here is the full `index.js`

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import Amplify from "aws-amplify"
import awsconfig from "./aws-exports"
Amplify.configure(awsconfig)

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

And full `App.js`

```jsx
import React, { useState } from 'react';
import './App.css';

import { API, graphqlOperation } from "aws-amplify"
import { createTodo } from "./graphql/mutations"
import { listTodos } from "./graphql/queries"

function App() {
  const [todoName, setTodoName] = useState('')
  const [todoItems, setTodoItems] = useState([])

  const addTodo = async () => {
    await API.graphql(
      graphqlOperation(createTodo, { input: { name: todoName } })
    )
    setTodoName('')
    updateTodos()
  }

  const handleChange = (evt) => {
    setTodoName(evt.target.value)
  }

  const updateTodos = async () => {
    const allTodos = await API.graphql(graphqlOperation(listTodos))
    setTodoItems(allTodos.data.listTodos.items)
  }

  updateTodos()

  return (
    <div className="App">
      <input type="text" value={todoName} onChange={handleChange} />

      <button onClick={addTodo}>Add ToDo</button>

      <ul>
        {todoItems.map((item) => {
          return <li key={item.id}>{ item.name }</li>
        })}
      </ul>
    </div>
  );
}

export default App;
```