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:
By the end of this tutorial, you will know how to consume a REST API in React using multiple methods.
To follow along, you need the following:
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.
1git clone https://github.com/pusher/react-rest-api-tutorial.git 2 cd react-rest-api-tutorial
npm install
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:
With that out of the way, we can continue with actually consuming a REST API and rendering some data based on the response.
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.
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.
npm install axios
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)
1// continues from above (1) 2function App() { 3 const [contacts, setContacts] = useState([]); 4 const [error, setError] = useState(null); 5 6// continues below (2)
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)
/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:
Axios
library.App.jsx
file as well as the useState
and useEffect
React hooks.https://jsonplaceholder.typicode.com/users
to fetch a list of people and saved the response in the state.<Contact />
component for each of them.Check out a snapshot of how your code should look at this point.
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.
npm install swr
useSWR
custom hook that allows us to manage our API requests —1import useSWR from 'swr' 2import Contact from './Contact' 3 4// continues below (1)
1// continues from above (1) 2 3const fetcher = (...args) => fetch(...args).then((res) => res.json()); 4 5// continues below (2)
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)
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:
useSWR
so we don’t have to bring in useState
and useEffect
To explore their other capabilities, check out SWR docs.
Check out a snapshot of how your code should look like at this point.
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.
npm i @tanstack/react-query
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);
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)
1// continues from above (1) 2 3const fetcher = () => 4 fetch("https://jsonplaceholder.typicode.com/users").then((res) =>res.json()) 5 6// continues below (2)
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.
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.
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.