How to Update REST APIs in React

Updating REST APIs in React involves sending HTTP mutation requests—such as POST, PUT, or PATCH—from your client-side application to a backend server. This article covers the most effective ways to perform these updates, demonstrating how to use the native Fetch API, the popular Axios library, and modern data-fetching tools like TanStack Query (React Query) to manage state, loading, and error conditions.

Choosing the Right HTTP Method

Before writing the React code, you need to use the correct HTTP method for your update:


Method 1: Using the Native Fetch API

The native fetch() browser API is the simplest way to update data without installing third-party packages. You trigger the API call inside an event handler (like a button click or form submission) and handle the promise.

import { useState } from 'react';

function UpdateUser({ userId }) {
  const [name, setName] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const handleUpdate = async (e) => {
    e.preventDefault();
    setIsLoading(true);

    try {
      const response = await fetch(`https://api.example.com/users/${userId}`, {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ name }),
      });

      if (!response.ok) {
        throw new Error('Failed to update user');
      }

      const data = await response.json();
      alert('Update successful!');
    } catch (error) {
      console.error(error);
      alert('An error occurred');
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <form onSubmit={handleUpdate}>
      <input 
        type="text" 
        value={name} 
        onChange={(e) => setName(e.target.value)} 
        placeholder="New Name"
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Updating...' : 'Update Name'}
      </button>
    </form>
  );
}

Method 2: Using Axios

Axios is a popular HTTP client that simplifies the syntax, automatically transforms JSON data, and provides better error handling than the Fetch API.

To install Axios:

npm install axios

Below is how you use Axios to perform a PUT request:

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

function UpdateProduct({ productId }) {
  const [price, setPrice] = useState('');

  const updatePrice = async () => {
    try {
      const response = await axios.put(`https://api.example.com/products/${productId}`, {
        price: parseFloat(price),
      });
      console.log('Success:', response.data);
    } catch (error) {
      console.error('Error updating price:', error.response?.data || error.message);
    }
  };

  return (
    <div>
      <input 
        type="number" 
        value={price} 
        onChange={(e) => setPrice(e.target.value)} 
        placeholder="New Price"
      />
      <button onClick={updatePrice}>Update Price</button>
    </div>
  );
}

Method 3: Using TanStack Query (React Query)

For production applications, managing loading states, error states, and cache synchronization manually becomes repetitive. TanStack Query provides a useMutation hook designed specifically for updating server data.

To install TanStack Query:

npm install @tanstack/react-query

Here is how you perform an update and automatically refresh the UI cache:

import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

const updateUserEmail = async ({ userId, email }) => {
  const response = await axios.patch(`https://api.example.com/users/${userId}`, { email });
  return response.data;
};

function UpdateEmailForm({ userId }) {
  const queryClient = useQueryClient();
  const [email, setEmail] = useState('');

  const mutation = useMutation({
    mutationFn: updateUserEmail,
    onSuccess: () => {
      // Invalidate and refetch the user data query to update the UI
      queryClient.invalidateQueries({ queryKey: ['user', userId] });
      alert('Email updated successfully!');
    },
    onError: (error) => {
      console.error('Mutation error:', error);
    }
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    mutation.mutate({ userId, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="email" 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
        placeholder="New Email"
      />
      <button type="submit" disabled={mutation.isPending}>
        {mutation.isPending ? 'Saving...' : 'Update Email'}
      </button>
      {mutation.isError && <p>Error: {mutation.error.message}</p>}
    </form>
  );
}

Key Best Practices for API Updates

  1. Implement Loading States: Always disable update buttons and show a loading spinner/text to prevent users from double-submitting forms.
  2. Handle Errors Gracefully: Do not assume your API calls will always succeed. Catch errors and show user-friendly error messages.
  3. Keep Client Cache Synchronized: If you update a resource, ensure your local React state or cache (like TanStack Query) updates as well to reflect the changes immediately.
  4. Secure Your Requests: Send authorization headers (such as Bearer tokens) if the API endpoints require authentication.