Consuming REST APIs in React using FetchAPI, Async/Await, and useEffect.

Consuming REST APIs in React using FetchAPI, Async/Await, and useEffect.

Introduction.

Making API requests and consuming data from different APIs is one of the significant backbones of any dynamic web-based project. This can be done in different ways, using Axios, a JavaScript library used to make HTTP Requests, and Fetch API, a browser-built-in web API.

In this article, we will learn about REST APIs, how to consume them in a React project using the fetch() API method, handling API request promises using Async/Await, and optimizing the process for our React App using the useEffect Hook.

What is an API?

Before we move forward, let's understand what an API is and why it is crucial in building software.

API stands for Application Programming Interface. It is a set of definitions and protocols for building and integrating application software. It is sometimes referred to as a contract between a provider of information and a user of that information, establishing the content required from the consumer (the request) and the content required by the producer (the response).

One analogy that helps understand this concept is imagining you're in a restaurant, a waiter caters to your order, and a Chef in the kitchen fulfills that order by preparing the meal you've requested. Now in web terminologies, you, the customer, are the CLIENT, the order you've made is the REQUEST, the Chef in the kitchen is the WEB SERVER, the meal that is brought back to you is the RESPONSE, and the waiter is the API.

What is a REST API?

A REST API (also known as RESTful API) is an application programming interface (API or web API) that conforms to the constraints of REST architectural style and allows for interaction with RESTful web services. The full meaning of REST is Representational State Transfer, and American Computer Scientist Roy Fielding created it.

A RESTful API transmits a representation of the resource's state to the requester or endpoint when a client request is made. One of the various formats, including JSON (Javascript Object Notation), HTML, XLT, Python, PHP, or plain text, is used to send this information or representation via HTTP. JSON is the most widely used file format because both humans and machines can read it.

Example of a REST API Response

The code below is a JSON object response received from making a GET request to the GitHub Open API. This API will be used to further explain more concepts down the line in this article.

{
    "login": "EmmanuelOloke",
    "id": 16335826,
    "node_id": "MDQ6VXNlcjE2MzM1ODI2",
    "avatar_url": "https://avatars.githubusercontent.com/u/16335826?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/EmmanuelOloke",
    "html_url": "https://github.com/EmmanuelOloke",
    "followers_url": "https://api.github.com/users/EmmanuelOloke/followers",
    "following_url": "https://api.github.com/users/EmmanuelOloke/following{/other_user}",
    "gists_url": "https://api.github.com/users/EmmanuelOloke/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/EmmanuelOloke/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/EmmanuelOloke/subscriptions",
    "organizations_url": "https://api.github.com/users/EmmanuelOloke/orgs",
    "repos_url": "https://api.github.com/users/EmmanuelOloke/repos",
    "events_url": "https://api.github.com/users/EmmanuelOloke/events{/privacy}",
    "received_events_url": "https://api.github.com/users/EmmanuelOloke/received_events",
    "type": "User",
    "site_admin": false,
    "name": "Emmanuel Oloke",
    "company": null,
    "blog": "http://twitter.com/I_am_Pope",
    "location": "Port Harcourt, Nigeria",
    "email": null,
    "hireable": null,
    "bio": "Developer and Tech Enthusiast",
    "twitter_username": "I_am_Pope",
    "public_repos": 45,
    "public_gists": 0,
    "followers": 12,
    "following": 16,
    "created_at": "2015-12-17T11:34:36Z",
    "updated_at": "2022-12-13T12:34:13Z"
}

FetchAPI and its Parameters.

The fetch() API is a JavaScript method built-in for retrieving resources from a server or API endpoint. Although it is comparable to XMLHttpRequest, the fetch API offers a more robust and flexible feature set.

The path or URL to the resource you wish to fetch is a required argument for the fetch() API function. Whether the request is successful(fullfilled) or not(rejected). It returns a promise that points to the response to the request. The second argument is the init option, which is optional.

Fetch API Parameters

  • resource - This is the path to the resource you wish to fetch. It may be a request object or a link to the resource path.

  • init - This represents an object containing any custom setting you’ll like to provide for your fetch() request. Below are the possible options the init object can contain.

  1. methods - This specifies the type of HTTP request method, e.g., GET, POST, PUT, DELETE, etc.

  2. headers- This specifies any headers you would like to add to your request. It is contained within a Headers object or an object literal with string values.

  3. body - The body that you want to add to your request. This can be a Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object. Note that a request using the GET or HEAD method cannot have a body.

  4. mode - The mode you want to use for the request e.g cors, no-cors, or same-origin.

  5. credentials - This represents the request credentials you want to use for the request. If you consider sending cookies automatically for the current domain, this option must be provided.

Using Fetch API in React with Async/Await.

In the example below, we are going to build a React component that makes a request using the GitHub Open API to fetch details about a user's GitHub repositories.

We will use the Async/Await approach, which enables asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to configure promise chains explicitly.

import React, { useState } from 'react';

const Repos = () => {
    const [repos, setRepos] = useState([]);
    const url = 'https://api.github.com/users/EmmanuelOloke/repos';

    const getRepos = async (API_URL) => {
        const response = await fetch(API_URL);
        const fetchedRepos = await response.json();
        setRepos(fetchedRepos);
    }
}

export default Repos;

To explain the above code, I've created a react functional component Repos in which I set a state variable repos using react's in-built useState() hook for state management. This state variable contains an empty array initially, but the eventual purpose is to hold the data which will be returned as a response from the API request we make, which will be an array of objects, where each object in the array represents a single repo of the user.

I declared a variable url which is a string containing the absolute path to the API endpoint we will fetch from.

An asynchronous function getRepos was declared, which will take in an API_URL parameter, pass that parameter into the fetch() method prepended with the await keyword because it is an asynchronous operation that returns a promise and saves the result in the response variable.

We use the .json() method on response to extract the JSON body content from response. This returns a Promise, hence the need for us to prepend with the await keyword, and then save the result in the fetchedRepos variable.

Note that despite the method being named json(), the result is not JSON but is instead the result of taking JSON as input and parsing it to produce a JavaScript object.

Finally, we use the second value of the useState() hook, which updates the state to update the repos state to the content of fetchedRepos

Bringing it all together with useEffect.

The useEffect() hook is one of the core React hooks added to the library in 2018. Its primary purpose is to allow us to perform side effects in our React Functional Components.

useEffect() accepts two arguments, a function, and an optional second parameter which is an array of dependencies.

To fully understand how useEffect() works, let's compare it with the equivalent React Class Component Lifecycle methods, which includes componentDidMount, componentDidUpdate, and componentWillUnmount

Equivalent of componentDidMount in useEffect().

To perform the equivalent of componentDidMount using the useEffect()hook:

useEffect(() => {
    //... We perform whatever side effect function we want to do here.
});

By simply passing just the callback function to the useEffect() hook without the second argument, we perform the equivalent of the componentDidMount lifecycle method in class components. This way, the hook will be called every time the component updates or re-render.

Equivalent of componentDidUpdate in useEffect().

To perform the equivalent of componentDidUpdate using the useEffect()hook:

useEffect(() => {
    //... We perform whatever side effect function we want to do here.
}, [dependency1, dependency2, dependency3]);

By passing a dependency array as the second argument to our useEffect() hook, we perform the equivalent of componentDidUpdate. During the lifetime of the component, the hook will fire if any of the dependencies in the array changes.

Note: If we pass an empty array of dependency as the second argument, the hook will fire once after a re-render.

Equivalent of componentWillUnmount in useEffect().

To clean up after a component unmounts using the useEffect() hook, we need to return a function inside the hook's callback function and specify the cleanup actions we want to perform.

useEffect(() => {
    //... We perform whatever side effect function we want to do here.
    return () => {
        //... Clean up action to be taken.
    }
});

Now back in our Repos component, we can use the useEffect() hook to execute the getRepos function once the component mounts like so:

import React, { useState, useEffect } from 'react';

const Repos = () => {
    const [repos, setRepos] = useState([]);
    const url = 'https://api.github.com/users/EmmanuelOloke/repos';

    const getRepos = async (API_URL) => {
        const response = await fetch(API_URL);
        const fetchedRepos = await response.json();
        setRepos(fetchedRepos);
    }

    useEffect(() => {
        getRepos(url);
    });

    return (repos.map(repo => (
            <div>{repo.name}</div>
        ))  
    );
}

export default Repos;

In the above code, we added to the existing functionalities using the useEffect() hook. We declared the hook and passed only the callback function to it as a parameter. Inside it, we execute the getRepos() function and passed the url variable to it as its argument. By doing this, we ensure that the getRepos() function gets executed once the component mounts, and then we return a div containing the name of each repo by using the JavaScript map() function on the repos array.

Conclusion

Consuming different APIs is something we frequently do as Software Engineers, so an in-depth understanding of how it works is essential. I hope you enjoyed reading through this article and that you found it helpful in one way or the other. If you have any questions or suggestions, even if it's just a typo you noticed or something else you'd like to point out, please feel free to leave a comment. I'll reply to them all. Cheers!!!