Adding a Amplify Database to your React App with Amplify


If you want to add data to your React app but don’t want to have to build an API then this is the article for you. Learn how you can use AWS Amplify to create and access a DynamoDB table in the easiest way possible.

We’re starting with our React and Amplify app from the last tutorial. If you didn’t follow along with that then you can read it here or clone the code by running this command.

git clone --single-branch --branch amplify-login git@github.com:SamWSoftware/amplify-react-tutorial-project.git

In this video we’ll be having a look at how we can use AWS amplify inside our react app to allow us to access our Amplify Database on the back end using GraphQL. To start we need to go into the terminal and run:

amplify add api

This will start us in a set of CLI options, asking us a few configuration questions:

What kind of API we want to use: GraphQL
The name of the API: songAPI
How we want to authenticate the API: Amazon Cognito User Pool
Advanced Settings: No, I am done
Do you have a schema: No
What kind of schema do you want: Single object with fields

After a little setup we are asked if we want to edit our new schema. We want to say yes. This opens the GraphQL schema which we’re going to update to be the schema listed here.

type Song @model {
        id: ID!
        title: String!
        description: String!
        filePath: String!
        likes: Int!
        owner: String!
    }

With our schema set up we’re going to run amplify push which will compare out current amplify setup with that on our AWS account. As we’ve added a new TypeScript API we’ll have changes so will be asked if we want to continue with the changes.

Once we’ve selected Yes then we’re put into another set of options.

Do we want to generate code for our GraphQL API: Yes
Which Language: JavaScript
File pattern for the new files: src/graphql/**/*.js
Generate all operations: Yes
Maximum statement depth: 2

This will now deploy all of the changes to AWS and also set up the new request files in our React app. This does take a few minutes to do.

Once that is completed we can go into our App.js file and rename it to be App.jsx. We now need to write a function in here to get the list of songs from our new database. This function calls the graphql api passing in the opperation of listSongs. We also need to add a new state to the App component.

const [songs, setSongs] = useState([]);
    
    const fetchSongs = async () => {
        try {
            const songData = await API.graphql(graphqlOperation(listSongs));
            const songList = songData.data.listSongs.items;
            console.log('song list', songList);
            setSongs(songList);
        } catch (error) {
            console.log('error on fetching songs', error);
        }
    };

We now need to add or update a few imports to our file to get this working

import React, { useState, useEffect } from 'react';
    import { listSongs } from './graphql/queries';
    import Amplify, { API, graphqlOperation } from 'aws-amplify';

The listSongs is one of those functions created by amplify to help us access our data.

Now we want this function to be called once when the component renders, but not every time that it re-renders. To do this we use useEffect but make sure to add a second parameter of [] so that it only gets triggered once.

useEffect(() => {
        fetchSongs();
    }, []);

If we now start our app using npm start and then go to the app we can open the console and see a log of song list []. This means that the useEffect has called the fetchSongs which is console logging out the result, but currently there is nothing in the Amplify Database.

To correct this we need to go into our AWS account and add find Dynamo. We should find a new table called something like Song-5gq8g8wh64w-dev. This currently has no data so we need to add some. For now we’re going with manually creating new data in here. Under Items click Create item and then make sure the dropdown in the top left shows text. If it shows tree then just click it and change it to tree. We can then make the data to go into that row.

We start with the GraphQL schema, giving the row some data for each attribute but also need to add a createdAt and updatedAt value. This can be found using the console and typing new Date() .toISOString() and copying the result of that. You should end up with an object like this:

{
      "id": "gr4334t4tog345ht35",
      "title": "My First Song",
      "description": "A test song for our amplify app",
      "owner": "Sam Williams",
      "filePath": "",
      "likes": 4,
      "createdAt": "2020-08-13T07:01:39.176Z",
      "updatedAt": "2020-08-13T07:01:39.176Z"
    }

If we save that new object then we can go back into our app and refresh the page. We should now be able to see our data in the console.log.

We can now use this data in our app to show the list of songs that we just got. Replace the existing text of song list with this set of JSX.

<div className="songList">
        {songs.map((song, idx) => {
            return (
                <Paper variant="outlined" elevation={2} key={`song${idx}`}>
                    <div className="songCard">
                        <IconButton aria-label="play">
                            <PlayArrowIcon />
                        </IconButton>
                        <div>
                            <div className="songTitle">{song.title}</div>
                            <div className="songOwner">{song.owner}</div>
                        </div>
                        <div>
                            <IconButton aria-label="like">
                                <FavoriteIcon />
                            </IconButton>
                            {song.like}
                        </div>
                        <div className="songDescription">{song.description}</div>
                    </div>
                </Paper>
            );
        })}
    </div>

This code is mapping over each song in the list and renders a new Paper one for them with all the details we need. We’re using the MaterialUI library to help make this look nice for us so we need to make sure to run npm install --save @material-ui/core @material-ui/icons to install those packages and then add them to the imports at the top of the file too:

import { Paper, IconButton } from '@material-ui/core';
    import PlayArrowIcon from '@material-ui/icons/PlayArrow';
    import FavoriteIcon from '@material-ui/icons/Favorite';

With this, if we save and reload our app we now get this:

Whilst this is ok, we can update the CSS to make it look far better. Open up your App.css file and change it to this.

.App {
        text-align: center;
    }
    
    .App-logo {
        height: 10vmin;
        pointer-events: none;
    }
    
    .App-header {
        background-color: #282c34;
        min-height: 5vh;
        display: flex;
        align-items: center;
        justify-content: space-around;
        font-size: calc(10px + 2vmin);
        color: white;
    }
    
    .App-link {
        color: #61dafb;
    }
    
    .songList {
        display: flex;
        flex-direction: column;
    }
    
    .songCard {
        display: flex;
        justify-content: space-around;
        padding: 5px;
    }
    
    .songTitle {
        font-weight: bold;
    }

Now we get it looking like this – much better.

Now we’ve got one item in the Amplify Database so only get one record. If we go back into Dynamo and create a new item or duplicate the existing one then we can see how multiple songs look.

Now that we can get the data, what about updating that info. For this we are going to add the ability to like a song. To start this we can add an onClick function to the icon button that we have for the likes.

<IconButton aria-label="like" onClick={() => addLike(idx)}>
        <FavoriteIcon />
    </IconButton>

You may have realised that there is this idx property that we haven’t see before. That is short for index. Where we do the songs.map we can update it slightly to get the position of each item in the list. We can also use this idx to add a key to the top level Paper in that map to remove an error we get from React.

{songs.map((song, idx) => {
        return (
            <Paper variant="outlined" elevation={2} key={`song${idx}`}>
                ...
            </Paper>
        )}
    )}

With the new index and the onClick function call we now need to make the addLike function. This function needs to take the index of the song to find the correct song, update the number of likes. It then removes some fields that can’t be passed into the updateSong operation before calling that operation.

const addLike = async idx => {
        try {
            const song = songs[idx];
            song.like = song.like + 1;
            delete song.createdAt;
            delete song.updatedAt;
    
            const songData = await API.graphql(graphqlOperation(updateSong, { input: song }));
            const songList = [...songs];
            songList[idx] = songData.data.updateSong;
            setSongs(songList);
        } catch (error) {
            console.log('error on adding Like to song', error);
        }
    };

Once the song has been updated in the Amplify Database, we need to get that update back into our state. We need to clone the existing songs using const songList = [...songs]. If we just mutate the original list of songs then React wouldn’t have re-rendered the page. With that new song list, we call setSongs to update our state and we’re done with the function. We just need to add one more import to the top of the file which we get from the mutators that Amplify created:

import { updateSong } from './graphql/mutations';

Now when we click on the like button on a song, it is updated in state and in the Amplify Database.

Sam Williams

Sam is a Serverless Obsessive who runs Complete Coding, helping developers learn Serverless and companies make the most of the competitive advantage that Serverless gives them. Previous projects include: - Designing a chat platform that currently resolves over 250,000 customer inquiries a month for international retailers and local government - Architecting a backend system to support a 3D clothing visualisation tool - Building a solution to manage millions of dollars of communication software - Designing a "Video Editing in the Cloud" platform, enabling everyone from movie studios to indie film makers to harness the power of the Cloud - without needing to be cloud experts. - Launching a profitable Serverless Startup in under 30 days He has also been teaching cloud-based software development for 5 years and has taught Serverless development to thousands of people. The Complete Coding Youtube channel now has over 15,000 subscribers and over 1 million views

Recent Posts