Easier way of data fetching with Axios in Vue3
Published: 2023-10-04 11:40:43

Data fetching is one of the most important parts of front-end programming. There are quite a few libraries and packages like fetch API, TanStack, etc. Axios is my choice but is it easy and efficient enough?

We used to use Axios traditionally like the code below
```javascript
axios({
  url:"/some-api-url",
  method: "GET",
}).then((res) => {
     // more response code here
  }).catch((err) => {
     // more error code here
  })
```
Or maybe like this

```javascript
axios
  .get("/some-api-url")
  .then((res) => {
     // more response code here
  }).catch((err) => {
     // more error code here
  })
```

These codes are the same and simple, right? But what if I like to add some loaders?
Okay That's simple as well, I will code this one

```javascript
const loader = ref(false)

loader.value = true
axios
  .get("/some-api-url")
  .then((res) => {
     // more response code here
  }).catch((err) => {
     // more error code here
  })
  .finally(() => {
     loader.value = false
  })
```

See! It's simple!

But what if I want to have 10 different requests on a single page or component? well, I have to make 10 different loader refs and manually set them true and false! And what if I want to set an alert for the request if its response is an error? Yes, it's complicated and dirty now!

So I decided to code a custom hook for Axios to make my life easier

First, I created **useAxios.js** file and created a function in it like so:

```javascript
const useAxios = ({ url, data: userData, headers }) => {
  const loading = ref(false) // for making loaders easily
  const error = ref(null) // getting error if there is any
  const data = ref(null) // the response of the request
  const status = ref(null) // status code
  const response = ref(null) // response obj (data, headers, etc.)
  const errorMessage = ref(null) // error message to show
}
```

Then I created the request function so I can invoke it whenever I want with the given arguments

```javascript
const useAxios = ({ url, data: userData, headers }) => {
  const loading = ref(false) // for making loaders easily
  const error = ref(null) // getting error if there is any
  const data = ref(null) // the response of the request
  const status = ref(null) // status code
  const response = ref(null) // response obj (data, headers, etc.)
  const errorMessage = ref(null) // error message to show

  const request = async (method) => {
    // Implementing loading functionality
    loading.value = true

    // Don't forget to put in try/catch statement!
    try {
      const res = await axios({ url, method, userData, headers })

      // Response data
      data.value = res.data

      // Response Obj
      response.value = res

      // Response status code
      status.value = res.status
    } catch (e) {
      // Response Obj
      response.value = e.response

      // Error obj
      error.value = e

      // Error status code
      status.value = e.response.status

      // Error message
      errorMessage.value = e.messaage
    }

    loading.value = false
  }
}
```

create axiosInstance file, so we can set default base url and add manual configuration into axios

**axiosInstance.js**
```javascript
import axios from 'axios'
import axiosConfig from '../axios.config.js'

const axiosInstance = axios.create()

axiosInstance.defaults.baseURL = "http://localhost:8000"

export default axiosInstance
```

Logic is done, we just need some more functions to make it much easier, when we want to make a request, So we add request functions with different methods and return them 

And that's it, the final code should be look like this:

**useAxios.js**
```javascript
import axiosInstance from './axiosInstance.js'


const alert = useAlert()

const objectToQueryString = (obj, withQuestionMark = true) => {
    let r = []

    Object.keys(obj).forEach((key) => {
        if (obj[key] !== null && obj[key] !== undefined) {
            r.push(key + `=` + obj[key])
        }
    })

    return r.length > 0 ? `${withQuestionMark ? '?' : ''}${r.join('&')}` : null
}


const useAxios = ({
    url,
    data: userData,
    headers,
    name = null,
    notif = true,
    notification,
}) => {
    const loading = ref(false)
    const error = ref(null)
    const data = ref(null)
    const status = ref(null)
    const response = ref(null)
    const errorMessage = ref(null)
    const useNotificationForAllRef = ref(notif)

    const request = async (method, payload = {}, noLoading = false) => {
        if (!noLoading) {
            loading.value = true
        }

        const axiosObj = {
            method,
            headers,
        }

        if (method === 'GET') {
            if (objectToQueryString(payload)) {
                axiosObj.url = url + objectToQueryString(payload)
            } else {
                axiosObj.url = url
            }
        } else {
            axiosObj.url = url
            axiosObj.data = payload
        }

        try {
            const res = await axiosInstance(axiosObj)

            data.value = res.data
            response.value = res
            status.value = res.status
            loading.value = false

            // Add your alert here if you have any
            if (notif) {
                // TODO translate first then
                // alert({
                   // title: data.value.status,
                   // type: 'success',
                   // text: data.value.message,
                // })
            }

            return res.data
        } catch (e) {
            response.value = e.response
            error.value = e
            status.value = e.response.status
            errorMessage.value = e.response.data.message
            loading.value = false
	
	    // Add your alert here if you have any
            if (notif) {
                // TODO translate first then
                // alert({
                   // title: e.response.data.status,
                   // type: 'error',
                   // text: e.response.data.message,
               // })
            }

            throw e
        }
    }

    const getReq = async (data, noLoading = false) =>
        await request('GET', data, noLoading)
    const postReq = async (data, noLoading = false) =>
        await request('POST', data, noLoading)
    const putReq = async (data, noLoading = false) =>
        await request('PUT', data, noLoading)
    const patchReq = async (data, noLoading = false) =>
        await request('PATCH', data, noLoading)
    const deleteReq = async (data, noLoading = false) =>
        await request('DELETE', data, noLoading)

    const returnObj = {
        error,
        data,
        response,
        status,
        errorMessage,
        loading,
        getReq,
        postReq,
        putReq,
        patchReq,
        deleteReq,
    }

    if (name) {
        // name sanitization

        returnObj['get' + name] = getReq
        returnObj['post' + name] = postReq
        returnObj['put' + name] = putReq
        returnObj['patch' + name] = patchReq
        returnObj['delete' + name] = deleteReq
        returnObj['loading' + name] = loading
        returnObj['data' + name] = data
        returnObj['error' + name] = error
    }

    return returnObj
}

```

Let's see, how can we use it in a component

**component.js**
```javascript
<template>
  <div>
    <LoadingComponent loading="loading" />
    <button @click="clickFunction">
      Click me
    <button />
  <div />  
<template />

<script setup>
import useAxios from '~/utils/useAxios'

const { postReq, getReq, loading, data } = useAxios({
  url: '/product',
  name: 'Product',
})

const { putProduct2, loadingProduct2 } = useAxios({
  url: '/product/2',
  name: 'Product2',
})

const clickFunction = async () => {
  await putProduct2({
    name: "Product 2 new name"
  })

  // will get products in /api/v2/product
  await getReq()

  // you can also get in variable
  const productsData = await getReq()
}
<script />
```

Hope you like it!
Thanks for reading :)