Axios 🚀

Udemy John

GET Request

we can do either axios.get(url) or axios(url) for GET request.

import { useEffect } from 'react'
// limit, if 429 wait for 15 min and try again

import axios from 'axios'
const url = 'https://course-api.com/react-store-productss'

const fetchData = async () => {
  try {
    const { data } = await axios(url)
    console.log(data)
  } catch (error) {
    // if you just do error here, it won't show you the object
    console.log(error.response) 
  }
}

const FirstRequest = () => {
  useEffect(() => {
    fetchData()
    console.log('first axios request')
  }, [])

  return <h2 className="text-center">first request</h2>
}
export default FirstRequest

Headers

We need to provide headers for POST and PATCH request. Some times in some apis we also need to provide headers for GET request.

const { data } = await axios(url, {
    headers: {// if we don't put this headers and accept json, we get a text instead of json
      Accept: 'Application/json',
    },
})
import { useState } from 'react'
import axios from 'axios'

const url = 'https://icanhazdadjoke.com/'
// Accept : 'application/json'

const Headers = () => {
  const [joke, setJoke] = useState('random dad joke')

  const fetchData = async () => {
    try {
      const { data } = await axios(url, {
        headers: {
          Accept: 'Application/json',
        },
      })
      setJoke(data.joke)
    } catch (error) {
      console.log(error.response)
    }
  }

  const fetchDadJoke = async () => {
    fetchData()
  }

  return (
    <section className="section text-center">
      <button className="btn" onClick={fetchDadJoke}>
        random joke
      </button>
      <p className="dad-joke">{joke}</p>
    </section>
  )
}
export default Headers

In GET Request, the headers will be 2nd parameter like this

axios(url, {headers:{}} )
// OR
axios.get(url, {headers:{}} )

In POST/PATCH Request, since we will also be sending the body, the body will be 2nd param and headers will be 3rd param

axios.post(url, data, {headers:{}} )

POST Request

import { useState } from 'react'
import axios from 'axios'
const url = 'https://course-api.com/axios-tutorial-post'

const PostRequest = () => {
  const [name, setName] = useState('')
  const [email, setEmail] = useState('')

  const handleSubmit = async (e) => {
    e.preventDefault()
    try {
      const resp = await axios.post(url, { name, email })
      console.log(resp)
    } catch (error) {}
  }

  return (
    <section>
      <h2 className="text-center">post request</h2>
      <form className="form" onSubmit={handleSubmit}>
        <div className="form-row">
          <label htmlFor="name" className="form-label">
            name
          </label>
          <input
            type="text"
            className="form-input"
            id="name"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div className="form-row">
          <label htmlFor="email" className="form-label">
            email
          </label>
          <input
            type="email"
            className="form-input"
            id="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <button type="submit" className="btn btn-block">
          login
        </button>
      </form>
    </section>
  )
}
export default PostRequest

Global Defaults

We can add global defaults to axios. That means every time we make a axios request, that functionality will be added. For example, we can add these defaults in one file and import them

axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] =
  'application/x-www-form-urlencoded';

Let's say we add first one (headers), then every request will accept application/json and we don't need to add to individual requests.

We need to create a new JS file for axios configuration like this

axios/global.js
import axios from 'axios'

axios.defaults.headers.common['Accept'] = 'application/json'

We have created axios folder and inside that, we have a file global.js where all axios defaults go.

Then we need to import this file in main file App.js or index.js like this

App.js or index.js
import './axios/global'

Now we can use axios in the component file as ususal

GlobalInstance.js
import { useEffect } from 'react'
import axios from 'axios'
const productsUrl = 'https://course-api.com/react-store-products'
const randomUserUrl = 'https://randomuser.me/api'

const GlobalInstance = () => {
  const fetchData = async () => {
    try {
    
      /* both resp1 and resp2 will have request headers - application/json because
      of global settings done above */
      
      const resp1 = await axios(productsUrl) 
      const resp2 = await axios(randomUserUrl)
      console.log(resp1)
      console.log(resp2)
    } catch (error) {
      console.log(error.response)
    }
  }

  useEffect(() => {
    fetchData()
  }, [])

  return <h2 className="text-center">global instance</h2>
}
export default GlobalInstance

both resp1 and resp2 will have request headers - application/json because of global settings of axios. We might not need this setting to one of the individual axios request sometimes, so let's see how to solve that using custom instance

Custom Instance

Here instead of global instance we use custom instance where we can create axios functions that have some defaults and we can use it where we want

axios/custom.js
import axios from 'axios'

const authFetch = axios.create({
  baseURL: 'https://course-api.com', // this would be the root url
  headers: {
    Accept: 'application/json',
  },
})

export default authFetch
CustomInstance.js
import axios from 'axios'
import { useEffect } from 'react'
import authFetch from '../axios/custom'

const randomUserUrl = 'https://randomuser.me/api'

const CustomInstance = () => {
  const fetchData = async () => {
    try {
      // the full url will be baseURL + this one  = https://course-api.com/react-store-products
      const resp1 = await authFetch('/react-store-products') // this req has accept : application/json only and no text
      const resp2 = await axios(randomUserUrl) // this is normal axios request
    } catch (error) {}
  }

  useEffect(() => {
    fetchData()
  }, [])

  return <h2 className="text-center">custom instance</h2>
}
export default CustomInstance

The below request has application/json

// this req has accept : application/json only and no text
const resp1 = await authFetch('/react-store-products') 

The below second request is a normal axios request so it has both application/json and text

// this is a normal axios req that doesn't have any defaults
const resp2 = await axios(randomUserUrl) 

Interceptors

These are the axios functions that can be called before the request is sent and/or after getting the response.

Interceptors make more sense when we have complex applications for example if we have authentication in a big app. This is just an intro to interceptors and we can take a look later in projects as and when we need (I'll update this notes with more info as I come across it)

We will use CustomImplementation of axios and not global one in our example. We could also use Global ones, just fyi.

Component that uses interceptor that is used in App.js

Component that uses Interceptor
import { useEffect } from 'react'
import authFetch from '../axios/interceptor'

const url = 'https://course-api.com/react-store-products'

const Interceptors = () => {
  const fetchData = async () => {
    try {
      const resp = await authFetch('/react-store-products') // we already have first half of url as baseURL in authFetch
      console.log(resp)
    } catch (error) {}
  }

  useEffect(() => {
    fetchData()
  }, [])

  return <h2 className="text-center">interceptors</h2>
}
export default Interceptors

The request and response interceptors in authFetch

axios/interceptor.js
import axios from 'axios'

const authFetch = axios.create({
  baseURL: 'https://course-api.com',
  // Lets put the below in interceptor
  //   headers: {
  //     Accept: 'application/json',
  //   },
})

// REQUEST INTERCEPTOR
authFetch.interceptors.request.use(
  (request) => {
    // we can see in network tab if accept is set to 'application/json'
    request.headers.common['Accept'] = 'application/json'
    console.log('Request sent')
    return request // we always need to return the request from interceptor
  },
  (error) => {
    return Promise.reject(error)
  }
)

// RESPONSE INTERCEPTOR
authFetch.interceptors.response.use(
  (response) => {
    console.log('Response recieved')
    return response
  },
  (error) => {
    return console.log(error.response)
  }
)

export default authFetch

Now where this interceptors comes in handy? We can think of Authentication as one use-case.

Let's say if we get some type of response from backend, it will hit response interceptor. If the user is not logged in, then we get the 401 which can be caught in this response interceptor and we can log out the user immediately and set the state accordingly.

// RESPONSE INTERCEPTOR
authFetch.interceptors.response.use(
  (response) => {
    console.log('Response recieved')
    return response
  },
  (error) => {
    console.log(error.response)
    // for authentication this will be 401, but since we are
    // changing url for now it is 404, but behaviour would be same
    if (error.response.status === 404) {
      // do something - we will generally update the state and may be logout the user
      console.log('Not found')
    }
    return Promise.reject(error)
  }
)

Last updated