AWS Amplify + React Native - Building A Blog App | Part 3

AWS Amplify + React Native - Building A Blog App | Part 3

·

7 min read

Welcome to the third and final part of this series. If you've come here directly, it is advisable to read the first two parts before you continue.

Part 1

Part 2

Table Of Contents

Now that we have handled the Navigation for the app using Cognito, we should move on the final phase, which is the implementation of making and receiving requests to fetch and show data on the app. Since the app that we’re making is a blog app, it should be able to display existing blog posts, create new posts and obtain edit/delete functionalities, all within the app.

We tried doing this before from the dashboard by making GraphQL calls in the first part. We’re essentially doing the same thing, but straight from the app.

Let’s first find the place where our first post was saved. To do that:

Go to your AWS console -> Click on Services -> Click on DynamoDB on the left panel (our default DB) -> Click on Tables.

image2.png

You should see a Table already created (if you’ve been following along). Access the Table and click on Items. There, you will be able to see the post that we created in the first part, just sitting there casually. We created this post using a test account.

Let’s create some with a real account.

Fetching Posts

First, login with the main account in the app. Now in the code, make your way to HomeScreen.js. There, you’ll see a constant with the name data, which is an array of dummy objects in the form of the schema that we’ve defined. We’re going to replace this data with the actual data stored in DynamoDB.

Import these statements into the HomeScreen.js file:

import { API, graphqlOperation } from 'aws-amplify'
import * as queries from ‘./../graphql/queries’

The above line retrieves all queries from the DB, which we can filter at a later stage. To fetch posts in the app, add the following code:

async function fetchPosts() {
    try {
      const { data } = await API.graphql(graphqlOperation(queries.listPosts));
      setData(data.listPosts.items);
    } catch (err) {
      console.log(err, 'error fetching todos');
    }
  }

  useFocusEffect(
    useCallback(() => {
      fetchPosts();
    }, [])
  );

This component, whenever mounted, will fetch the posts by making a query to list all posts using queries.listPosts.

We have also wired this data to our UI. We need to create a new State variable to store the data that we’ll be getting. Let’s create a variable and call it data. Initially, it will be an empty array.

const [data, setData] = useState([]);

...but when we get the data, we will display the data using:

const { data } = await API.graphql(graphqlOperation(queries.listPosts));
      setData(data.listPosts.items);

Remember to replace the hard coded DATA state with this current state in the UI.

Creating A New Post

We’re going to try the same approach used above to be able add new posts through the app. Adding a new post would essentially be a mutation.

Navigate to CreateScreen.jsand import the same GraphQL query operations statements from above.

Then, add the following lines of code:

import * as mutations from '../graphql/mutations';
const handleSubmit = async () => {
    setLoading(true);
          try {
        const { title, description } = formState;
        await API.graphql(
          graphqlOperation(mutations.createPost, {
            input: { title, description, createdBy: user.email }
          })
        );
        setFormState({});
        setLoading(false);
      } catch (err) {
        setLoading(false);
        console.log('error creating post:', err);
      }

  };

We are importing all mutations from GraphQL. handleSubmit is an async function that will take input fields and return them as title, description and specify the owner of the post. As far as the ownership of the user is concerned, Amplify stores all attributes like Email, User Pool ID, JWT tokens etc. for every user and uses this data to identify the current user. We don’t have to use this data for our operations at all. We’ll just store the username and email in the context that we’re using.

Navigate to the AppNavigation.js file and find AppNavigation() and add setUser as a constant and add the following line:

setUser({ username: user.username, email: user.attributes.email });

Also, add the following line in AuthContext.js:

 const [user, setUser] = useState({});

To consume this data, first add useContext in the import statement and then add the following line of code:

import { AuthContext } from '../AuthContext';

Display the user name with a welcome message by returning user.username.

image1.png

To handle the situation where a different user signs in, navigate to LoginScreen.js, add setUser in the context and define setUser() in the Sign In module.

FInally, to set the ownership of each added post, import the following lines in CreateScreen.js:

 const { user } = useContext(AuthContext);
import * as mutations from '../graphql/mutations';

Then add an owner:user.email field in graphqlOperation().

Note that at this point of time, I updated the existing schema to remove some mistakes and add a new operation. I replaced the owner field to createdBy and added an update operation.

At this point, you should be able to create new posts and check them in the DynamoDB table.

Editing Posts

Let’s implement the editing and deleting function for all the posts on the app. While writing these functionalities, it is very important to remember that these functionalities should only be available to the owners of the post and no-one else.

The first thing to do is import an icon in the UI that represents the edit option. Then, navigate to HomeScreen.js and add the following line:

editable={user.email === item.createdBy}

This implements the condition that the edit option is only available on a post when its owner is accessing it. The next is to make sure that when the edit button is tapped, the fields should be prefilled with the existing data, that can be edited and should not be empty.

In HomeScreen.js, import routes, add the navigation & id prop in const Item = () and add the following line to the <TouchableOpacity> for the edited function:

  <TouchableOpacity
            onPress={() => navigation.navigate(routes.Create, { id })}
            style={[t.pX2]}
          >
            <AntDesign name="edit" size={20} color={color.gray900} />
          </TouchableOpacity>
        )}

Also pass navigation to the Item component as a prop.

This will navigate the screen to the edit screen. To populate it with existing data, check the route parameter in CreateScreen.js, get the id of the post and copy the following code in CreateScreen.js:

import * as queries from '../graphql/queries';
 const { id } = route?.params || {};
 useEffect(() => {
    getPostById();
  }, []);

  const getPostById = async () => {
    try {
      const { data } = await API.graphql(
        graphqlOperation(queries.getPost, {
          id
        })
      );
      setFormState(data.getPost);
    } catch (err) {
      console.log('error creating post:', err);
    }
  };

Post this, all you need to do is make an update request under handleSubmit:

const handleSubmit = async () => {
    setLoading(true);
    if (id) {
      try {
        const { title, description } = formState;
        await API.graphql(
          graphqlOperation(mutations.updatePost, {
            input: { id, title, description }
          })
        );
        setFormState({});
        setLoading(false);
      } catch (err) {
        setLoading(false);
        console.log('error creating post:', err);
      }
    } else {
      try {
        const { title, description } = formState;
        await API.graphql(
          graphqlOperation(mutations.createPost, {
            input: { title, description, createdBy: user.email }
          })
        );
        setFormState({});
        setLoading(false);
      } catch (err) {
        setLoading(false);
        console.log('error creating post:', err);
      }
    }
  };

This will make the posts fully editable and update the changes in the posts. Of course, with the condition we implemented in the beginning, these changes can only be made by the owner of the posts.

Deleting Posts

Deleting posts is pretty much the same as editing posts. A prop named onDeletePost exists in the code already. So, you can just call it:

const handleDelete = async () => {
    try {
      await API.graphql(
        graphqlOperation(mutations.deletePost, {
          input: { id }
        })
      );
      setFormState({});
    } catch (error) {
      console.log(error, 'error');
    }
  };

  const headerProps = {
    navigation,
    title: id ? 'Edit Post' : 'Create Post',
    ...(id && { onDeletePost: handleDelete })
  };

Put the call in a try-catch blog where we will pass the id of the post that needs to be deleted. That’s it! You’ll be able to delete the post through the account of the user who own them.

Conclusion

It has been quite a journey but I’m glad that you stuck around till the end. At this stage, we’ve successfully implemented navigation, authentication and the basic functionalities of a blog app using AWS Amplify and its robust features. There is still a lot to explore and experiment with, so you’re more than welcome to do that and share your experiences here in the comments section.

Thank you so much for reading. See you around!

This experiment has been carried out by Sankhadeep Roy , Engineering Manager and AWS Enthusiast GeekyAnts and documented by Digvijay Wanchoo , A. Manager, Marketing & Communications GeekyAnts