Frontend
.......................................................................................................
Signup to be notified when the Fluent React course
launches in MARCH 2025 .
(and get started with a 5-part free React course in the mean time.)
...........................................................................................

1: Intro

 

As you build more React apps, you will find that your components need to make API calls to a backend server to GET, POST, UPDATE and DELETE data needed to serve as your components' state.

 

In this guide, we will take a look at how to use the `Axios` HTTP library to make CRUD calls to a backend API server for data to use in your React components.

 

2: Why API Calls?

But, why do you need to make calls to an API server to get or set data?

 

There are a few reasons, including:

  • Get Existing Data to use in your components. You may be rendering a list of items, but can't hardcode these into the component. This is because items could be added, updated or deleted from the backend API server's database. An item may have been updated or deleted from the database.

    But without an API call, there will be no way to know. This will necessitate fetching the fresh centrally-persisted data and not use stale hard-coded data.

    This is the work of the HTTP `GET` method.

  • Add New Data: You may have a component with a form that collects data (user registration, posting a new comment, creating a new blog post, writing a new status update, uploading a photo or video, etc.)

    That data needs to be saved somewhere persistent that can be accessed now and in the future for use by the current React app and, possibly, other apps.

    Making a `POST` call over HTTP to the backend API server will help achieve this.

  • Update Existing Data: The body of the blog post you published may need to be updated.

    Or, a user may need to change the city and country listed in his/her social profile.

    Or, a picture or post may need to be marked as "unpublished" so that it can't be seen by other than the original poster (or by anyone, for that matter).

    Or, some other piece of data may need to be changed.

    Whatever it is, making an `UPDATE` call to the API server will help ensure that you render updated and currently-accurate data in your React components.

  • Remove Unneeded Data: Does a comment need to be removed? Does a picture have to be deleted from an album? Does a conversation thread have to be removed from a group/forum?

    A `DELETE` call over HTTP to the backend API server will take care of this.

    Whatever you need to do to make sure that you manage the dynamic data you will be using in your components, an API call will go a long way in helping make it a reality.

 

3: Why Axios?

There are a few NPM packages that you can choose to use to make HTTP calls to your backend API server.

These include:

  • Fetch API: This is a global JavaScript function that you can use to make HTTP calls from any valid JavaScript file, whether in a backend or frontend app. It needs no installation or special configurations to work.

  • axios-http: This is one of the most popular HTTP libraries in the JavaScript/NodeJS community. It can be used in a backend app (using the ExpressJS framework, for instance) or a frontend app like React or Angular.

 

There are other libraries and/or packages, but these two are some of the most commonly-used.

 

So, why use Axios?

 

Well, that's because, with Axios, you:

  • can make more custom configurations of your base API-calling object

  • have more than one way of making API calls. We will go into details about this later.
So, without much ado, let's get to making API calls with Axios in our React app.

4: Installation and Setup

Before we start making API calls, let's do some mandatory "house-keeping".

 

We will:

  • create a new React app (using either CRA or Vite)

  • install `Axios` (so that we can use it within the React app)

  • organize the folders and files to hold the API-calling logic that will use `Axios`.
So:

  1. Create a React App: Create a new React app using either "Create React App" (CRA) or Vite. I will call mine `blogios` (to make it sound cool and akin to the `axios` name.)

  2. Install Axios: `cd` into the React app (which I will, henceforth, just call "blogios") and install `Axios` using either:

    • `npm install axios`: if you are using NPM.

    • `yarn add axios`: if you are using Vite

      Upon successful installation, you should find that `axios`:

    • is listed inside both the `package.json` and `package-lock.json` files inside the root directory of your React app

    • now has a folder inside your React app's `node_modules/` folder (which would be `node_modules/axios/` and contains the folders and files for the Axios package.)

  3. Create an API Folder: Next, create an `apis` folder inside the `src/` folder of your React app.

    The new `src/apis/` folder will now hold any files relating to your API-calling logic.

    You can call this folder `api`, `myApi` or anything else that is sensible and memorable (for you and your fellow/future team members.)

    I just called it `apis` in this case to give it a memorable and responsible name. The name doesn't really matter from a technical perspective.

    So:

    
    cd src && mkdir apis
    

5: Create an Axios Instance

 

Once you install `axios` in your React app, you can now now import and use the default `axios` object from the package to make API calls over HTTP.
 
But, you can go a step further and, instead of using the default `axios` object, you can create an instance of it and customize it with your own API backend's `baseURL` and headers (with your API authentication tokens, `Content-Type`, etc.)

 

So, let's create a an `apis/index.js` file and create this custom Axios instance in there.

 


//  apis/index.js
import axios from 'axios';

const HttpRequestHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Content-Type': 'application/json',
}


const myAxios = axios.create({
    baseURL: 'https://jsonplaceholder.typicode.com/',
    headers: HttpRequestHeaders,
})

export default myAxios;
From now on, whenever we want to make HTTP calls to our API backend server, we can use our custom `myAxios` instance instead of the default `axios` object.

 

6: Create the API Object

 

Next, let's create an API class/object that we can use to make calls to a specific endpoint.

 

The logic here is that:

  • Your API backend may have multiple API endpoints that your React app may need to call. We may have the `/users/`, `/posts/`, `/comments/`, and so on, API endpoints. So, we will create one API class/object for each of these endpoints. You can create these using JavaScript classes (which we will use here) or using custom React hooks (which are basically JavaScript functions; we won't cover that here.)

  • Each API object should have one function/method whose sole purpose is to make exactly one CRUD call to the API endpoint. So, an API class method can make an HTTP call to fetch/get one blog post from the backend. But the same function should not be used to make a `POST` call to the API server to submit/create a new blog post. That's a no-no.

  • You can reuse the endpoint-specific API object (and its methods) in as many places and components of your React app as you want. The type of input (fed into the object) and output (i.e. response from the API server) will be of the same type.

 

In our case, we are building a simple blogging app. Our React app will have a couple of components including
 
  • `BlogPostList`: (which, as you may have guessed, will list the blog posts available).

  • `NewBlogPost`: This component will enable you to write up and publish a new blog post.

 

So, let's create a `BlogPostApi` class to handle calls to the `/posts/` API endpoint.

 

Let's a create an `apis/BlogPostApi.js` file to hold the `BlogPostApi` object that we will be creating.

 

Best Practice: Name your API objects and their files the same, so that (future) you or your team members won't have to guess which API objects a particular file contains. Make it simple and recognizable for you and others.

 

So:
 
  1. Create the API file

    
    cd src/apis && touch BlogPostApi.js
    
  2. Create the logic for the API calls to the `posts/` endpoint

    
    import myAxios from ".";
    
    class BlogPostApi {
        //  A custom object to make CRUD calls 
        //  against the 'jsonplaceholder.typicode.com/posts' endpoint.
    
        static getAllPosts(){
            return myAxios.get(`posts/`);
        }
    
        static getPost(post_id){
            return myAxios.get(`posts/${post_id}`);
        }
    
        static newPost(new_post){
            return myAxios.post(`posts`, new_post)
        }
    
        static updatePost(post_id, updated_post){
            // Replace the existing post (in the API backend) with the new `updated_post` object
            return myAxios.put(`posts/${post_id}`, updated_post)
        }
    
        static deletePost(post_url){
            return myAxios.delete(`posts/${post_id}`);
        }
    
    }
    
    export default BlogPostApi;
    
    Remember to export the API object so that we can import and reuse it in other parts of our React app. This would be in the app's components, state management files, and so on, depending on the needs, structure and complexity of your React app.

     

    Do note that you can also create class methods to handle filtering for matching objects in your API backends.

     

    For example, you may want to create a class method to make a `GET` API call, filter for all the matching blog posts and return them as the HTTP Response from the call.

     

    So, you can have a class method (for the `BlogPostApi` object) like this:

     

    
    static getPostsInCategory(categoryId){
            return myAxios.get(`posts?category=${categoryId}`)
        }
    
    This method will make an API call and filter the blog posts to get only those that belong to a certain blog category. This would be useful if you have a `CategoryComponent` which renders only the posts in a matching category (say, "poetry", "tech", "business", etc.) We don't need it in our example here, which is why we omitted it.

     

    7: Use the API Object in the Components

    Now that you have created the `BlogPostApi` API class/object, let's look at how to properly use it in our React app's components.

     

    Let's do this step-by-step:

     

    1. Create a `components` folder inside the `src/` directory of your React app, like this:

     

    
    cd src && mkdir components
    
    This new `src/components/` folder will hold the files for the components of our app.

     

    7.1: Make `GET` Calls to the API Backend

    2. Create a `BlogPostList.js` file inside the new `src/components/` folder.

     

    
    cd components && touch BlogPostList.js
    
    3. Create a namesake `BlogPostList` component and add its logic and UI to the `BlogPostList.js` file

     

    
    import React, {useState, useEffect} from 'react'
    import BlogPostApi from '../apis/BlogPostApi'
    
    const BlogPostList = () => {
        const [posts, setPosts] = useState([])
        const [apiError, setApiError] = useState("")
    
        function getPosts(){
            BlogPostApi.getAllPosts()
                .then(response => setPosts(response.data))
                .catch(error => setApiError("Error! No blog posts were found."))
        }
    
        useEffect(() => {
            getPosts()
        }, [])
    
        return (
            <div>
                {apiError && (<p style={{color:'red'}}> {apiError} </p>)}
    
                <h3> List of Blog Posts</h3>
                <ul>
                {posts.map(post => (
                    <li key={post.id}>
                        <a href={post.id}> {post.title} </a> 
                    </li>
                    ))}
                </ul>
            </div>
        )
    }
    
    export default BlogPostList
    
    Now, open your app in the browser. If you have an internet connection (and have correctly coded the API object and component), your app should:

     

    • make a call to the "https://jsonplaceholder.typicode.com/posts/" API endpoint

    • return and render a list of blog posts found at that endpoint (which could be a lot. But we won't be handling pagination here, unfortunately.)

    If you encounter errors, please, retrace your steps (from the `BlogPostApi` object to the `BlogPostList` component code) and fix them. Use the browser's "Dev Tools" to fish for bugs and warnings that may be causing your app not to work properly.
     
    If you encounter an unusually "stubborn" error, please, let us know in the comments section at the end of this guide.

     

    7.2 Make API Calls to DELETE Objects from the API Endpoint

    In the `BlogPostList` component we just covered, you saw how we can make API calls to fetch (aka "GET") data from a backend server.

     

    Now, take a look at how to delete objects from your components and let that reflect in your API backend (where the object will also be deleted) at the same time.

     

    We will use the same `BlogPostList` component and the `BlogPostApi` API object to demonstrate how to make an HTTP `DELETE` call to the API server.

     

    So, let's add a simple "X" button next to each blog post in the list. Once that button is clicked, an API call will be made to the API backend to remove the associated post from the list of published posts.

     

    Let's update the `BlogPostList` component to reflect that:

     

    
    // src/components/BlogPostList.js file
    
    import React, {useState, useEffect} from 'react'
    import BlogPostApi from '../apis/BlogPostApi'
    
    const BlogPostList = () => {
        const [posts, setPosts] = useState([])
        const [apiError, setApiError] = useState("")
        const [postDeleted, setPostDeleted] = useState(false)
        const [postDeleteError, setPostDeleteError] = useState("")
    
        function getPosts(){
            BlogPostApi.getAllPosts()
                .then(response => setPosts(response.data))
                .catch(error => setApiError("Error! No blog posts were found."))
        }
    
        function deletePost(post_id){
            BlogPostApi.deletePost(post_id)
                .then(response => setPostDeleted(true))
                .catch(err => setPostDeleteError("Sorry, that post could not be deleted."))
        }
    
        useEffect(() => {
            getPosts()
        }, [postDeleted])
    
        return (
            <div>
                {apiError && (<p style={{color:'red'}}> {apiError} </p>)}
    
                <h3> List of Blog Posts</h3>
                {postDeleteError && <p style={{color:'orange'}}> {postDeleteError} </p>}
                <ul>
                {posts.map(post => (
                    <li key={post.id}>
                        <button onClick={(e) => deletePost(post.id)}>X</button>
                        <a href={post.id}> {post.title} </a> 
                    </li>
                    ))}
                </ul>
            </div>
        )
    }
    
    export default BlogPostList
    

    You will notice with the updated `BlogPostList` component that:

    • There is a delete ("X") button next to each blog post (and an `onClick` event listener attached to each button)

    • Clicking the "delete"/"X" button will make the `onClick` event listener call the `deletePost()` event handler function to execute

      The `deletePost()` function will:

      • make an HTTP `DELETE` call to the backend API server using the `BlogPostApi.deletePost()` method

      • set the `postDeleted` component state to `true` if successful (it is `false` by default)

      • populate the `postDeleteError` state with an error message (if the `DELETE` API call was not successful.)

    • We passed the `postDeleted` state to the `useEffect()` hook's dependency array `[]` so that once a blog post is deleted, it triggers the component to make another API call to get the (updated) list of blog posts and re-render the component's UI.

    • The errors and/or updated list of blog post (minus the deleted one) are rendered in the component's UI.

     

    7.3 Make API Calls to CREATE New Objects

     

    Next, let's take a look at how to make an API call to submit new data to the API backend via the HTTP `POST` method.

     

    To achieve this, we will create a new aptly-named `NewBlogPost` component. It will use the same `BlogPostApi` object to make the API call to submit the new blog post's data.

     

    So:

     

    1. Create a new `NewBlogPost.js` file inside the `src/components/` folder of your React app.

      
      cd src && touch NewBlogPost.js
      
    2. Create the `NewBlogPost` component (logic, UI, etc.)

      
      import React, {useState} from 'react'
      import BlogPostApi from '../apis/BlogPostApi'
      
      const NewBlogPost = () => {
          const [title, setTitle] = useState("")
          const [body, setBody] = useState("")
          const [formError, setFormError] = useState("")
          const [newPost, setNewPost] = useState({})
          const [apiError, setApiError] = useState("")
          const [apiSuccess, setApiSuccess] = useState("")
      
          const submitPost = (e) => {
              e.preventDefault();
              if(title && body){
                  let newBlogPost = { title, body }
                  let blogPostJson = JSON.stringify(newBlogPost)
      
                  BlogPostApi.newPost(blogPostJson)
                      .then(response => {
                          setNewPost(response.data)
                          setApiSuccess("You have successfully-created a new blog post.")
                          setTitle("")
                          setBody("")
                      })
                      .catch(err => setApiError("Sorry, a new post could not be created."))
              }else {
                  setFormError("Please, fill out all form fields correctly.")
              }
          }
      
          return (
              <div>
      
                  <h3> Add a New Blog Post </h3>
                  {apiError && <p style={{'color':'orange'}}> {apiError} </p>}
                  {apiSuccess && <p style={{'color':'green'}}> {apiSuccess}: {newPost.title} </p>}
      
                  <form onSubmit={submitPost}>
                      {formError && <p style={{color:'red'}}> {formError} </p>}
                      
                      <input 
                          type="text"
                          value = {title}
                          onChange={(e) => setTitle(e.target.value)}
                          placeholder='Post Title'
                      />
                      <textarea
                          rows={10}
                          cols={13}
                          placeholder="Body of Post"
                          value={body}
                          onChange={(e) => setBody(e.target.value)}
                      />
      
                      <button type="submit"> Create New Post </button>
                  </form>
              </div>
          )
      }
      
      export default NewBlogPost

 
Our new `NewBlogPost` component:

renders a form that has a "title" and "body" form fields

  • uses an `onChange` event listener to "listen for"/detect changes in a form field and set the value of the respective state to the new form entry/input inline. (i.e. within the JSX markup, not in an external function).

  • checks that all form fields are valid (i.e. filled in) before making an API call; if not, it populates and renders the `formError` state in the component's UI.

  • uses a `submitForm()` event handler to make an API call (using the `BlogPostApi.newPost()` method) to submit the JSONified data of the form-submitted blog post to the API backend server.

  • updates the appropriate states of the component depending on the results of the API call:

    • If successful: the `apiSuccess` and `newPost` states are populated and rendered in the UI

    • If failed: the `apiError` state is populated and rendered in the component's UI

  • shows a few instance of the "if...else" conditional rendering logic of JSX (which we used when conditionally-rendering any of the form- and API-related error or success messages in the UI of the component.)

  • demonstrates how to do both inline event handling (such as with the `title` and `body` form fields) and event handling using an external function (which we implemented using the `submitForm()` event handler function.)

  • has a little bit of the approach to proper form processing in React (both in the UI/JSX and in the `submitForm()` function.)

Please, review the code for the `NewBlogPost` component and the bulleted list of notes above to get a grip on how to do both form processing and API calls from a React component.

7.4 Make UPDATE API Calls using Axios from your React Components

I will let you do the honor on this one! :)

Here are some pointers to get you going:

  1. Create a `BlogPostDetail` component which should make an API call to GET details about a single blog post. (Hint: use the `BlogPostApi.getPost()` method)

  2. Render the details of the blog post you got back from the API into the `return()` statement of the `BlogPostDetail` component.

  3. Add an "edit" button (using a "pencil" icon for proper imagery) next to the title and/or body of the blog post

  4. Clicking on the "edit" button should open a form `input` field (for the title) or `textarea` (for the body) so that a user can edit the title or body of the current blog post.

  5. Add a "Save" button next to the form fields so that a user can click on it to "save" the new values of the form field that was edited.

  6. When the "Save" button is clicked (after editing either the `title` or `body` fields of the blog post), trigger an API call to the backend server to submit the edited blog post and replace the former post with the edited one.

    Here are a few hints to help you with this:

    1. Add an `onChange` event listener to each "edit" button to call a function (inline) to set the new value of the edited form field. See how we did that with the `title` and `body` fields in the `NewBlogPost` component.

    2. Add an `onClick` event listener to the "Save" button which should call a `submitEditedPost()` event handler function to create and submit the JSONified data of the edited blog post. Use the `BlogPostApi.updatePost()` method to do this.

  7. Render appropriate success or error messages in the UI based on the outcome of the API call to the backend server.

Please, see the Make API Calls to CREATE New Objects section for the nitty gritty of he we did this for creating a new blog post. Emulate that to submit the edited blog post.

If you encounter errors or bugs (or blank, unrendering screens), please, debug your code, use Google and StackOverFlow and fix them even if you have to skip siesta.

I wish you all the best!

Alternative Usage of Axios

Throughout this developer guide, we have focused on using Axios via custom-defined JavaScript class methods. This usage allows you to create custom instances of the `axios` object (which we did with `myAxios` in the `apis/index.js` file).

But there are two other alternative ways of using Axios.

These are:

a. Direct Usage of the `axios` Object

In this case, instead of creating a custom Axios instance and class for our API endpoints (like we did in this guide), you will just import the `axios` object from the Axios package. Then, you will pass the API URL, data, HTTP method, authentication tokens, headers and so on directly to the `axios` object.

Many dev tutorials you will find on Youtube or on blogs use this approach.

It is less reusable than what we did throughout this guide (with the custom Axios instance and JavaScript class), but it useful and handy enough if, for example, you are just going to make one or two API calls and don't need to go through the whole song-and-dance of creating a custom Axios instance, defining a JavaScript class (and methods) for each API endpoint and so on.

So, consider your technical needs and see which option works for you best.

By the way, you can also create an Axios instance (like `myAxios`, in our case) and use that to make API calls without creating a JavaScript class for each API endpoint (like we did wuth the `BlogPostApi` class.)

Let's implement a simple example by showing you an alternative way of making the `POST` call to submit the new blog post from the `NewBlogPost` component we worked on earlier in this guide.

Open the `NewBlogPost` component file and update it like this: See official `axios-http` docs


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

const NewBlogPost = () => {
    const [title, setTitle] = useState("")
    const [body, setBody] = useState("")
    const [formError, setFormError] = useState("")
    const [newPost, setNewPost] = useState({})
    const [apiError, setApiError] = useState("")
    const [apiSuccess, setApiSuccess] = useState("")

    const submitPost = (e) => {
        e.preventDefault();
        if(title && body){
            let newBlogPost = { title, body }
            let blogPostJson = JSON.stringify(newBlogPost)

            // BlogPostApi.newPost(blogPostJson)
            //     .then(response => {
            //         setNewPost(response.data)
            //         setApiSuccess("You have successfully-created a new blog post.")
            //         setTitle("")
            //         setBody("")
            //     })
            //     .catch(err => setApiError("Sorry, a new post could not be created."))
            
            axios({
                url: '/posts/',
                method: 'POST',
                data: blogPostJson,
                headers: {

                }
            })
        }else {
            setFormError("Please, fill out all form fields correctly.")
        }
    }

    return (
        <div>
            <h3> Add a New Blog Post </h3>
            {apiError && <p style={{'color':'orange'}}> {apiError} </p>}
            {apiSuccess && <p style={{'color':'green'}}> {apiSuccess}: {newPost.title} </p>}
            <form onSubmit={submitPost}>
                {formError && <p style={{color:'red'}}> {formError} </p>}
                
                <input 
                    type="text"
                    value = {title}
                    onChange={(e) => setTitle(e.target.value)}
                    placeholder='Post Title'
                />
                <textarea
                    rows={10}
                    cols={13}
                    placeholder="Body of Post"
                    value={body}
                    onChange={(e) => setBody(e.target.value)}
                />

                <button type="submit"> Create New Post </button>
            </form>
        </div>
    )
}

export default NewBlogPost
 

b. Using Custom Hooks

Depending on your React app's use case, you may opt to create custom hooks (to make API calls with using Axios) instead of the class-based API objects that we have covered and focused on in this guide. Check online for a good tutorial on hoe to create custom React hooks to use with the Axios package.

If you need pointers on how I would do it, please, take a look at the "Create a Custom Hook to Make CRUD API Calls with the Fetch API" guide I put together to walk you through how to create React hooks to work with the `Fetch API`.

You can then replicate the same approach to implement Axios-focused custom hooks.

8: API Calling Best Practices

Now that we have covered how to use Axios to make API calls from your React app, you should know some best practices to abide by as you create your own frontend apps with API-calling logic.

Here are a few API best practices to follow as you build your React apps:

  • Use a Dedicated API Folder: If your app is simple enough, you can get away with throwing your API files right next to your React components.

    But it is better, even for a single-component app, to just create a separae folder to hold the files for the API classes/objects that you will be creating.

    In this guide, we created an `apis/` folder inside the app's `src/` directory. You can name the folder anything as long as it is simple, logical and easily-recognizable. Just create a separate folder for your API files. That's the point.

  • Keep API Methods Clean and Simple: If you are creating a JavaScript class to hold your API logic, keep each method of the API class clean and simple.

    Don't perform any calculations in there. Nor handle object instantiation or perform loops or conditional logic in there. Keep the body of the API class' method as a one-liner. Just like we did in this guide.

  • Perform Data Wrangling in Components: If you need to prepare the object to send to the API backend when making a `POST` call (like in the `NewBlogPost` component) or to make an `UPDATE` call (whether it is a `PATCH` or a `PUT` call), perform that object creation and JSONification (which we did using the `JSON.stringify()` function) logic inside your component files.

    Don't, for instance, pass the `title` and `body` fields' values to the API class' method to create the blog post object. Do that inside your components' logic and let the API method handle only sending that (JSONified) data to the API backend.

  • One Method per Use Case: No matter how tempting (or smart) it may appear, let each API class method perform just one single task.

    Do not `if...else` your way through the method.

    Take a look at the methods for the `BlogPostApi` class that we used in this guide and notice that each method performs just one task.

    Keep each API class method simple, lean and clean.

There are other pieces of insight that you will garner as you build more frontend/React apps with more complex API logic. When you do realize there is a certain "best way" to implement a certain API logic, document it and add it to your list of "API best practices".

 

Developer Guides Related to This One:

Please, share your insights, concerns and experiences about the topic and/or content of this article.