March 18, 2024

HTTP POST Method in React - fetch, Axios

In this post we'll see how to use HTTP POST method from React applications in order to create resources using API. We'll see usage of both fetch method in Fetch API and Axios with the help of examples.

  • fetch()- This method is used to call APIs across the network. GET is the default for fetch method.

    With fetch() method you pass the URI and the body in case of POST and it returns a promise that resolves with a Response object.

  • Axios- It is a promise-based HTTP Client for node.js and the browser.

For API calls JSONPlaceholder is used which is a free fake API for testing and prototyping.

POST request in React using fetch returning Promise

fetch() method takes one mandatory argument; path to the resource (API), it also accepts a second parameter, an init object that allows you to control a number of different settings. Here is the fetch method format for a POST request where you need to explicitly specify the method you are using and the object that you want to send.

fetch(url, {
    method: "POST", // *GET, POST, PUT, DELETE, etc.
    mode: "cors", // no-cors, *cors, same-origin
    cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
    credentials: "same-origin", // include, *same-origin, omit
    headers: {
      "Content-Type": "application/json",
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: JSON.stringify(data), // body data type must match "Content-Type" header
  });

In our example there is a Component named AddPost. With in the component there is a form to add new post data (userId, title and body). When the form is submitted, using fetch method a POST request is sent with the post data to the given URI. JSONPlaceholder returns the added post by including id (Post ID) to it.

AddPost.js

import { useState } from "react";

const AddPost = () => {
  const [formFields, setFormFields] = useState({
    title: '',
    body: '',
    userId: ''
  });
  const handleInputChange = (event) => {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    setFormFields((prevState) => {
        return {...prevState, [name]: value};
    });
  }
  const formSubmitHandler = (event) => {
    event.preventDefault();
    fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      // adding userId as a random int between 0-99
      body: JSON.stringify({...formFields, userId:Math.floor(Math.random() * 100)}),
      headers: {                
          'Content-type': 'application/json',                 
      },
    })    
    .then((response) => response.json())
    .then((resData) => console.log('Post added with id ', resData.id))
    .catch((error) => console.log(error.message))
  }
  return(
    <div className="container">
      <h2>Post</h2>
      <form onSubmit={formSubmitHandler}>
        <div className="mb-3 mt-3">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" placeholder="Enter Title" name="title" id="title" onChange={handleInputChange}></input>
        </div>
        <div className="mb-3">
          <label className="form-label" htmlFor="body">Body: </label>
          <input className="form-control" type="text" placeholder="Enter Post Content" name="body" id="body" onChange={handleInputChange}></input>
        </div>
        <button className="btn btn-info" type="submit" onClick={formSubmitHandler}>Add Post</button>
      </form>
    </div>
  )
}

export default AddPost;

In the code-

  1. useState() hook is used to maintain form data.
  2. fetch() method is called when the form is submitted, method is set as ‘POST’ and as the body form data is sent. Note that in the form title and post body is entered, user ID is added later.
  3. Content-Type is set as application/json which means that the request body format is JSON.
  4. When you create a resource in JSONPlaceholder, inserted post object is returned by the API which includes title, body, userId and also id (post ID, which is always 101).
  5. fetch() method returns a Promise, using promise chaining first the response object is converted to JSON data and then id is extracted from the returned post data.
  6. To handle any error there is also a catch method, which just logs the error message.
  7. Note that Bootstrap 5 is used for styling here. Refer Installing Bootstrap in React to know how to install Bootstrap in your React application.
React HTTP POST example

POST request using fetch with async/await

Using async/await is easier than using Promise with .then chaining so that is the preferred way. You can use try/catch to handle errors with async/await.

import { useState } from "react";

const AddPost = () => {
  const [formFields, setFormFields] = useState({
    title: '',
    body: '',
    userId: ''
  });
  const handleInputChange = (event) => {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    setFormFields((prevState) => {
      return {...prevState, [name]: value};
    });
  }
  const formSubmitHandler = async (event) => {
    event.preventDefault();
    try{
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
        method: 'POST',
        // adding userId as a random int between 0-99
        body: JSON.stringify({...formFields, userId:Math.floor(Math.random() * 100)}),
        headers: {                
          'Content-type': 'application/json',                 
        },
      });
  
      const responseData = await response.json();   
      if(!response.ok){
        throw new Error('Error while fetching post data');
      } 
      console.log('Post added with id ', responseData.id);
    }catch(error){
      console.log(error.message);
    }
  }
  return(
    <div className="container">
      <h2>Post</h2>
      <form onSubmit={formSubmitHandler}>
        <div className="mb-3 mt-3">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" placeholder="Enter Title" name="title" id="title" onChange={handleInputChange}></input>
        </div>
        <div className="mb-3">
          <label className="form-label" htmlFor="body">Body: </label>
          <input className="form-control" type="text" placeholder="Enter Post Content" name="body" id="body" onChange={handleInputChange}></input>
        </div>
        <button className="btn btn-info" type="submit" onClick={formSubmitHandler}>Add Post</button>
      </form>
    </div>
  )
}

export default AddPost;

Few points to note in the code-

  1. formSubmitHandler() method is async now.
  2. You need to wait for the response so await is used with the fetch method.
  3. Await is also used while converting response data to json.
  4. Check for the response status and throw an error if response status is not ok.
  5. Code is enclosed in try/catch block to handle the thrown error.

Showing error message With React fetch example

In the above React fetch method example with async/await, error message is logged to the console. If you want to display the error to the user then you can create one more component for Error. In the AddPost component have the state for Error and set the Error in case one is thrown.

You can also create a more generic Notification component that can show both success and error messages.

ErrorMessage.js

Uses the Bootstrap alert to show the error message.

const ErrorMessage = (props) => {
  return(
    <>
      <div className="alert alert-warning alert-dismissible fade show" role="alert">
        <p>{props.message}</p>
              
        <button type="button" className="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
      </div>
    </>
  )
}
export default ErrorMessage;

AddPost.js

Updated to have Error state variable and the logic to show error.

import { useState } from "react";
import ErrorMessage from "./ErrorMessage";

const AddPost = () => {
  const [formFields, setFormFields] = useState({
    title: '',
    body: '',
    userId: ''
  });
  const [error, setError] = useState();
  const handleInputChange = (event) => {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    setFormFields((prevState) => {
      return {...prevState, [name]: value};
    });
  }
  const formSubmitHandler = async (event) => {
    event.preventDefault();
    try{
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
          method: 'POST',
          // adding userId as a random int between 0-99
          body: JSON.stringify({...formFields, userId:Math.floor(Math.random() * 100)}),
          headers: {                
              'Content-type': 'application/json',                 
          },
        });
    
        const responseData = await response.json();   
        if(!response.ok){
          throw new Error('Error while fetching post data');
        } 
        console.log('Post added with id ', responseData.id);
    }catch(error){
      setError(error);
      //console.log(error.message);
    }
  }
  if(error){
    return (
      <ErrorMessage message={error.message}></ErrorMessage>
    )
  }
  return(
    <div className="container">
      <h2>Post</h2>
      <form onSubmit={formSubmitHandler}>
        <div className="mb-3 mt-3">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" placeholder="Enter Title" name="title" id="title" onChange={handleInputChange}></input>
        </div>
        <div className="mb-3">
          <label className="form-label" htmlFor="body">Body: </label>
          <input className="form-control" type="text" placeholder="Enter Post Content" name="body" id="body" onChange={handleInputChange}></input>
        </div>
        <button className="btn btn-info" type="submit" onClick={formSubmitHandler}>Add Post</button>
      </form>
    </div>
  )
}

export default AddPost;

Using HTTP POST method in React with Axios

If you want to use Axios library to make HTTP calls then the first thing is to install the Axios library.

Using npm

$ npm install axios

Using yarn

$ yarn add axios

POST request using Axios returning Promise - React Example

import axios from "axios";
import { useState } from "react";
import ErrorMessage from "./ErrorMessage";

const AddPost = () => {
  const [formFields, setFormFields] = useState({
    title: '',
    body: '',
    userId: ''
  });
  const [error, setError] = useState();
  const handleInputChange = (event) => {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    setFormFields((prevState) => {
      return {...prevState, [name]: value};
    });
  }
  const formSubmitHandler = (event) => {
    event.preventDefault();
    // adding userId as a random int between 0-99
    const data = {...formFields, userId: Math.floor(Math.random() * 100)};
    axios.post('https://jsonplaceholder.typicode.com/posts', data)
    .then(response => console.log('Post added with id ', response.data.id))
    .catch((error) => setError(error));
  }
  if(error){
    return (
      <ErrorMessage message={error.message}></ErrorMessage>
    )
  }
  return(
    <div className="container">
      <h2>Post</h2>
      <form onSubmit={formSubmitHandler}>
        <div className="mb-3 mt-3">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" placeholder="Enter Title" name="title" id="title" onChange={handleInputChange}></input>
        </div>
        <div className="mb-3">
          <label className="form-label" htmlFor="body">Body: </label>
          <input className="form-control" type="text" placeholder="Enter Post Content" name="body" id="body" onChange={handleInputChange}></input>
        </div>
        <button className="btn btn-info" type="submit" onClick={formSubmitHandler}>Add Post</button>
      </form>
    </div>
  )
}

export default AddPost;

Some points to note here-

  1. With Axios, post method automatically serializes the data to JSON format before sending it so no need to do it explicitly.
  2. Axios returns a Promise that resolves to a response object which has a data property containing the fetched data. That is why ID is extracted from response.data
    console.log('Post added with id ', response.data.id)
    
  3. With Axios default behavior is to reject every response that returns with a status code that falls out of the range of 2xx and treat it as an error.
  4. Error message gives a quick summary of the error message and the status it failed with.
  5. There is no change in ErrorMessage component. Please refer the above example to get the code for this component.

Using async/await with Axios Post - React Example

import axios from "axios";
import { useState } from "react";
import ErrorMessage from "./ErrorMessage";

const AddPost = () => {
  const [formFields, setFormFields] = useState({
    title: '',
    body: '',
    userId: ''
  });
  const [error, setError] = useState();
  const handleInputChange = (event) => {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    setFormFields((prevState) => {
      return {...prevState, [name]: value};
    });
  }
  const formSubmitHandler = async (event) => {
    event.preventDefault();
    // adding userId as a random int between 0-99
    const data = {...formFields, userId: Math.floor(Math.random() * 100)};
    try{
      const response = await axios.post('https://jsonplaceholder.typicode.com/posts', data);
      console.log('Post added with id ', response.data.id);
    }catch(error) {
      setError(error)
    }
  }
  if(error){
    return (
      <ErrorMessage message={error.message}></ErrorMessage>
    )
  }
  return(
    <div className="container">
      <h2>Post</h2>
      <form onSubmit={formSubmitHandler}>
        <div className="mb-3 mt-3">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" placeholder="Enter Title" name="title" id="title" onChange={handleInputChange}></input>
        </div>
        <div className="mb-3">
          <label className="form-label" htmlFor="body">Body: </label>
          <input className="form-control" type="text" placeholder="Enter Post Content" name="body" id="body" onChange={handleInputChange}></input>
        </div>
        <button className="btn btn-info" type="submit" onClick={formSubmitHandler}>Add Post</button>
      </form>
    </div>
  )
}

export default AddPost;

That's all for the topic HTTP POST Method in React - fetch, Axios. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment