How to Implement REST APIs in React

This article provides a practical guide on how to integrate and consume REST APIs in a React application. You will learn how to fetch data using the native Fetch API and the popular Axios library, manage component state for loading and errors, and implement modern data-fetching best practices.

1. Using the Native Fetch API

The simplest way to consume a REST API in React is by using the browser’s built-in fetch API inside the useEffect hook. This method requires no external dependencies.

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

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then((response) => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then((data) => {
        setUsers(data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default UserList;

2. Using Axios for API Requests

Axios is a popular promise-based HTTP client that simplifies API requests. It automatically transforms JSON data and has built-in protection against XSRF. To use it, first install it via npm: npm install axios.

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

function PostList() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts')
      .then((response) => {
        setPosts(response.data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <ul>
      {posts.slice(0, 5).map(post => (
        <li key={post.id}>
          <strong>{post.title}</strong>
          <p>{post.body}</p>
        </li>
      ))}
    </ul>
  );
}

export default PostList;

3. Handling POST, PUT, and DELETE Requests

In addition to fetching data (GET), you often need to send data to a REST API. Below is an example of implementing a POST request using Axios to add a new resource.

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

function CreatePost() {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await axios.post('https://jsonplaceholder.typicode.com/posts', {
        title,
        body,
        userId: 1,
      });
      console.log('Post Created:', response.data);
      alert('Post created successfully!');
    } catch (error) {
      console.error('Error creating post:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        placeholder="Title" 
        value={title} 
        onChange={(e) => setTitle(e.target.value)} 
      />
      <textarea 
        placeholder="Body" 
        value={body} 
        onChange={(e) => setBody(e.target.value)} 
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default CreatePost;

4. Modern Approach: React Query (TanStack Query)

For large-scale production applications, manual state management for APIs using useEffect can become complex. React Query is the industry standard for managing server state. It handles caching, background updates, and stale data automatically.

To use it, install the library: npm install @tanstack/react-query.

import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

const fetchUsers = async () => {
  const { data } = await axios.get('https://jsonplaceholder.typicode.com/users');
  return data;
};

function UsersWithQuery() {
  const { data, error, isLoading } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>An error occurred: {error.message}</p>;

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default UsersWithQuery;