Getting Started with AWS

Build a Full-Stack React Application

Create a simple web application using AWS Amplify

Module 5: Add Storage

In this module, you will add storage and the ability to associate an image with the notes in your app

Overview

Now that we have the notes app working, let's add the ability to associate an image with each note. In this module, you will use the Amplify CLI and libraries to create a storage service using Amazon S3. You will then update the GraphQL schema you created in the previous module to associate an image with each note. Finally, you will update the React app to enable image uploading, fetching, and rendering.

What you will accomplish

In this module, you will:
  • Create a storage service
  • Update a GraphQL schema
  • Update your React app

Key concepts

Storage service - Storing and querying for files, such as images and videos, is a common requirement for most applications. One option to do this is to Base64 encode the file and send as a string to save in the database. This comes with disadvantages, such as the encoded file being larger than the original binary, the operation being computationally expensive, and the added complexity around encoding and decoding properly. Another option is to have a storage service specifically built and optimized for file storage. Storage services like Amazon S3 exist to make this as easy, performant, and inexpensive as possible.

 Time to complete

10 minutes

 Services used

Implementation

  • To add image storage functionality, we'll use the Amplify storage category. You can keep the default selections for most of the options below, but be sure to select the create/update, read, and delete options individually by pressing the spacebar on each before pressing Enter to continue with the prompt.

    amplify add storage
    
    ? Select from one of the below mentioned services: Content (Images, audio, video, etc.)
    ? Provide a friendly name for your resource that will be used to label this category in the project: imagestorage
    ? Provide bucket name: <your-unique-bucket-name>
    ? Who should have access: Auth users only
    ? What kind of access do you want for Authenticated users? create/update, read, delete
    ? Do you want to add a Lambda Trigger for your S3 Bucket? (y/N) no
  • Next, open amplify/backend/api/notesapp/schema.graphql and update it with the following schema:

    type Note @model @auth(rules: [ { allow: public } ] ){
      id: ID!
      name: String!
      description: String
      image: String
    }

    Make sure to save the file.

  • Now that the storage service has been configured locally and we have updated the GraphQL schema, we can deploy the updates by running the Amplify push command:

    amplify push --y
  • Now that the backend has been updated, let's update the React app to add the functionality to upload and view images for a note. Open src/App.js and make the following changes.

    a. First add the Storage class and Image component to your Amplify imports:

    import { API, Storage } from 'aws-amplify';
    import {
      Button,
      Flex,
      Heading,
      Image,
      Text,
      TextField,
      View,
      withAuthenticator,
    } from '@aws-amplify/ui-react';

    b. Update the fetchNotes function to fetch an image if there is an image associated with a note:

    async function fetchNotes() {
      const apiData = await API.graphql({ query: listNotes });
      const notesFromAPI = apiData.data.listNotes.items;
      await Promise.all(
        notesFromAPI.map(async (note) => {
          if (note.image) {
            const url = await Storage.get(note.name);
            note.image = url;
          }
          return note;
        })
      );
      setNotes(notesFromAPI);
    }

    c. Update the createNote function to add the image to the local image array if an image is associated with the note:

    async function createNote(event) {
      event.preventDefault();
      const form = new FormData(event.target);
      const image = form.get("image");
      const data = {
        name: form.get("name"),
        description: form.get("description"),
        image: image.name,
      };
      if (!!data.image) await Storage.put(data.name, image);
      await API.graphql({
        query: createNoteMutation,
        variables: { input: data },
      });
      fetchNotes();
      event.target.reset();
    }
    

    d. Update the deleteNote function to delete files from storage when notes are deleted:

    async function deleteNote({ id, name }) {
      const newNotes = notes.filter((note) => note.id !== id);
      setNotes(newNotes);
      await Storage.remove(name);
      await API.graphql({
        query: deleteNoteMutation,
        variables: { input: { id } },
      });
    }

    e. Add an additional input to the form in the return block:

    <View
      name="image"
      as="input"
      type="file"
      style={{ alignSelf: "end" }}
    />

    f. When mapping over the notes array, render an image if it exists:

    {notes.map((note) => (
      <Flex
        key={note.id || note.name}
        direction="row"
        justifyContent="center"
        alignItems="center"
      >
        <Text as="strong" fontWeight={700}>
          {note.name}
        </Text>
        <Text as="span">{note.description}</Text>
        {note.image && (
          <Image
            src={note.image}
            alt={`visual aid for ${notes.name}`}
            style={{ width: 400 }}
          />
        )}
        <Button variation="link" onClick={() => deleteNote(note)}>
          Delete note
        </Button>
      </Flex>
    ))}

    g. Commit your changes and push to GitHub. Then wait for the build to complete to see your full app live!

    git add .
    git commit -m "Added graphql and storage"
    git push origin main
  • To test out the app, run the start command:

    npm start

    You should now be able to optionally upload an image for each note.

  • Removing individual services

    To remove individual services, you can use the Amplify remove command:

    amplify remove auth
    
    ? Choose the resource you would want to remove: <your-service-name>

    Then run the Amplify push command:

    amplify push

    Deleting the entire project

    To delete the project and the associated resources, you can run the Amplify delete command:

    amplify delete

Conclusion

You have deployed a web application using AWS Amplify! You have added authentication to your app allowing users to sign up, sign in, and manage their account. The app also has a scalable GraphQL API configured with an Amazon DynamoDB database, allowing users to create and delete notes. You have also added file storage using Amazon S3 so that users can upload images and view them in their app.

Was this page helpful?

Congratulations!

You successfully built a web application on AWS! As a great next step, dive deeper into specific AWS technologies and take your application to the next level.