How to consume a RESTful API in React

Introduction

How to consume a RESTful API in React

This brief tutorial will help you understand the concepts necessary for integrating a RESTful API into a React application.

React is still one of the most popular frontend frameworks out there in 2022, so knowing the various ways of integrating a RESTful API is a necessary skill for any frontend developer.

For this tutorial, we will build a simple contact list application to display a list of contacts’ names, emails, and tagline. Here's what we're going to do:

  • Fetch some data from an endpoint
  • Save the data in the state
  • Render said data

By the end of this tutorial, you will know how to consume a REST API in React using multiple methods.

Prerequisites

To follow along, you need the following:

  • Basic knowledge of React (versions > 16)
  • Basic understanding of what an API is
  • NPM installed on your computer

Set up our environment

There are multiple ways to start a React project but to keep things simple, I’ve gone ahead and set up a simple template that you can clone to get started.

  • Clone the ready-made React template and change it into the new directory.
1git clone https://github.com/pusher/react-rest-api-tutorial.git
2  cd react-rest-api-tutorial
  • Install all dependencies.
  npm install
  • Start the application.
  npm run dev

By now, you should have an instance of the app running on your local machine on port 5173. The template itself is fairly simple. It uses Vite, which is a build tool that aims to provide a fast and lean development experience for web projects.

You can explore other ways to start a new React project.

Take some time to look through the project and you’ll see a bunch of ready-made files and components.

For the sake of this tutorial, the only files that matter are:

  • main.jsx — The entry point of the app.
  • App.jsx — The main container of the app. Holds all other components.
  • Contact.jsx — Renders the details of a single contact.

With that out of the way, we can continue with actually consuming a REST API and rendering some data based on the response.

Consuming a REST API in React

As a quick refresher, a REST API is an API that maps a resource to an endpoint. Resource, in this case, means any piece of data necessary for an application to function. For example, a list of contacts resource(s) could be mapped to a /contacts endpoint.

To learn more about REST APIs, IBM has a good resource on it.

With that out of the way, the first thing we need to do is determine which API we’ll be consuming.

To avoid having to set up a whole server just for this tutorial, we’ll be consuming dummy data from a free, public API created for just this purpose, JSONPlaceholder.

It exposes a bunch of useful common endpoints and we’ll be consuming the /users endpoint that looks like https://jsonplaceholder.typicode.com/users.

Now that we have an endpoint, we have to determine how to actually consume it in our React app. There are multiple options available to choose from when it comes to making an API request in a React app:

  • Fetch/Axios – The easiest option is to use the Fetch API or use a library like Axios to make API requests. These are pretty bare-bones options without any advanced functionality like caching, deduping requests, etc.

  • SWR/React Query – For complex applications that require a more robust solution, it’s probably a better idea to go with a full-fledged data fetching library which offers a lot more functionalities out of the box.

We’ll explore both options below.

Using Fetch and Axios

Fetch API is a web standard that is implemented in all major browsers to allow for making network requests in a standardized way.

Axios is a third-party library that is functionally very similar to Fetch. It offers a few improvements over Fetch such as better backward compatibility, data transforms, or the ability to intercept a request before or after it is made.

For the reasons listed above, I’ll be using Axios in this tutorial but save for some minor differences (e.g., Axios serializes the response into JSON automatically) in usage, you should be able to use Fetch in almost exactly the same way.

  • Install the Axios library. Fetch doesn’t need to be installed as it ships with every major browser save for Internet Explorer.
npm install axios
  • Open the App.jsx file and import the Axios library as well as some React hooks:
1import { useState, useEffect } from 'react'
2import axios from 'axios'
3import Contact from './Contact'
4
5// continues below (1)
  • We need to store the response (or error, if any) from calling the API in React state. Initialize two state variables:
1// continues from above (1)
2function App() {
3  const [contacts, setContacts] = useState([]);
4  const [error, setError] = useState(null);
5
6// continues below (2)
  • We need to query the REST API endpoint when the App component is mounted and for that, we’ll use the useEffect hook:
1// continues from above (2)
2  useEffect(() => {
3    axios("https://jsonplaceholder.typicode.com/users")
4      .then((response) => {
5        setContacts(response.data);
6        setError(null);
7      })
8      .catch(setError);
9
10    /* Using Fetch
11    fetch("https://jsonplaceholder.typicode.com/users")
12      .then((response) => response.json())
13      .then((response) => {
14        setContacts(response);
15        setError(null);
16      })
17      .catch(setError);
18    */
19  }, []);
20
21// continues below (3)
  • Finally, we can render the data fetched from the /users endpoint. First, we check if there are any errors before rendering and then loop over the list of contacts in the state. For each contact, we will render one instance of the <Contact /> component:
1// continues from above (3)
2
3  if (error) return <p>An error occurred</p>
4
5  return (
6    <div className="App">
7      {contacts.map(({ id, name, email, company }) => (
8        <Contact
9          key={id}
10          name={name}
11          email={email}
12          tagline={company.catchPhrase}
13        />
14      ))}
15    </div>
16  );
17}
18
19export default App;

To recap, here’s what we’ve done:

  • Installed the Axios library.
  • Imported it in our App.jsx file as well as the useState and useEffect React hooks.
  • When the component mounted, we made a network request to https://jsonplaceholder.typicode.com/users to fetch a list of people and saved the response in the state.
  • Check for an error before rendering the list of people in state. If there’s an error, we render an error message.
  • Otherwise, we loop through the list of people in the state and render a <Contact /> component for each of them.

Check out a snapshot of how your code should look at this point.

Using SWR

The next option, SWR, is a more robust library that offers a ton of features for more advanced use cases. It integrates nicely with React Suspense, offers in-built pagination features, and caches data automatically, among a host of other useful features.

NOTE: This tutorial uses version 1.3 of SWR.

Using it is relatively simple as we’ll see shortly.

  • Install the SWR library like so —
npm install swr
  • Import the useSWR custom hook that allows us to manage our API requests —
1import useSWR from 'swr'
2import Contact from './Contact'
3
4// continues below (1)
  • SWR requires that we create a helper “fetcher” function that will query the endpoint for us. We will use Fetch in this function but according to the SWR docs, you can also use Axios or a GraphQL library:
1// continues from above (1)
2
3const fetcher = (...args) => fetch(...args).then((res) => res.json());
4
5// continues below (2)
  • Now that we have the plumbing set up, we can go ahead and consume the API.
1// continues from above (2)
2
3function App() {
4  const { data, error } = useSWR(
5    "https://jsonplaceholder.typicode.com/users",
6    fetcher
7  );
8
9// continues below (3)
  • Render the data from the endpoint when it becomes available.
1// continues from above (3)
2
3  if (error) return <p>An error occurred</p>;
4  if (!data) return <p>Loading</p>;
5
6  return (
7    <div className="App">
8      {data.map(({ id, name, email, company }) => (
9        <Contact
10          key={id}
11          name={name}
12          email={email}
13          tagline={company.catchPhrase}
14        />
15      ))}
16    </div>
17  );
18}
19
20export default App;

As you can see, using SWR is simple thanks to the very well-designed useSWR hook. There are some notable differences from the Fetch/Axios method above, notably:

  • SWR provides a custom hook, useSWR so we don’t have to bring in useState and useEffect
  • Error handling and loading state is handled for us

To explore their other capabilities, check out SWR docs.

Check out a snapshot of how your code should look like at this point.

Using React Query

The final option we’ll be exploring is React Query. It aims to be an opinionated way of fetching/updating data in React. And as a result, comes with a ton of useful features like parallel queries, pausing queries, automatic retries, etc.

NOTE: This tutorial uses version 4 of React Query.

Using it is slightly more complicated than the options above but is thankfully relatively still simple.

  • Install the React Query library
npm i @tanstack/react-query
  • We have to wrap the app in a “QueryClientProvider” which will make it possible to query endpoints from any component. Open the src/main.jsx file and wrap the App component like so:
1import React from "react";
2import ReactDOM from "react-dom/client";
3import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
5import App from "./App";
6
7import "./index.css";
8
9const queryClient = new QueryClient();
10
11ReactDOM.createRoot(document.getElementById("root")).render(
12  <React.StrictMode>
13    <QueryClientProvider client={queryClient}>
14      <App />
15    </QueryClientProvider>
16  </React.StrictMode>
17);
  • With the QueryClientProvider set up, we can now query endpoints in our App component. Open the src/App.jsx file and import the useQuery custom hook:
1import { useQuery } from '@tanstack/react-query'
2import Contact from "./Contact";
3
4// continues below (1)
  • Just like with SWR, we need a “fetcher” method that will actually make the request for us. Again, we will use Fetch but feel free to substitute with Axios or any other data fetching library:
1// continues from above (1)
2
3const fetcher = () =>
4  fetch("https://jsonplaceholder.typicode.com/users").then((res) =>res.json())
5
6// continues below (2)
  • With the fetcher set up, we can now go ahead and consume the endpoint:
1// continues from above (2)
2
3function App() {
4  const { isLoading, error, data } = useQuery(["contacts"], fetcher);
5
6// continues below (3)

Notice the first argument we pass to useQuery is an array containing "``contacts``". This is a unique identifier for this request so React Query knows how to differentiate it from other requests. This is necessary for some of the more advanced features like caching, pausing requests, etc.

  • Finally, we can then render our data from the endpoint:
1// continues from above (3)
2
3  if (isLoading) return <p>Loading</p>;
4  if (error) return <p>An error occurred</p>;
5
6  return (
7    <div className="App">
8      {data.map(({ id, name, email, company }) => (
9        <Contact
10          key={id}
11          name={name}
12          email={email}
13          tagline={company.catchPhrase}
14        />
15      ))}
16    </div>
17  );
18}
19
20export default App;

To explore their other capabilities, check out React Query docs.

Check out a snapshot of how your code should look at this point.

Conclusion

We’ve explored four ways of consuming an API in React. This is just the iceberg as there are a lot of things to consider when fetching data in a frontend application.

You can take this further by implementing a proper loading state, better error handling, caching data, etc.

Hopefully, this tutorial was useful and if you run into any issues following along or if you think this tutorial could be improved, please submit an issue.