Best Practices for Fetching Data in Vue.js

Binay MaharjanBinay Maharjan
4 min read

Fetching data in Vue.js applications is a common task. By following best practices, we can make your code more efficient, maintainable, and robust. Here are some of the best practices and common ways for fetching data in Vue.js used by almost all Vue developers.

Setting Up the Project
First, let's use the Vue CLI to create a new Vue.js project. If you haven't installed the Vue CLI yet, you can do so by using the following command:

npm install -g @vue/cli

Next, create a new project:

vue create best-practice

Navigate to the project directory:

cd best-practice

Now, let's add Axios to our project:

npm install axios

Axios is a popular HTTP client for making requests. It's promise-based and works well with Vue.js. It simplifies the process of sending asynchronous HTTP requests to a server and also handles the response. Axios supports features such as interceptors, handling request and response headers, and handling different data types, like JSON. It is widely used in web development to fetch data from APIs and interact with servers.

Separate API Calls into Services

Remember to abstract API calls into service files to keep your components clean and later helps in code reuse. For example, create a userService.js to handle user-related API calls:

// userService.js
import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  withCredentials: false,
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json'
  }
});

export default {
  getUsers() {
    return apiClient.get('/users');
  }
};

Use Composables for Reusable Logic

With Vue 3's Composition API, you can create composable functions to encapsulate reusable logic. This is especially useful for data fetching logic.

// composables/useUsers.js
import { ref, onMounted } from 'vue';
import userService from '@/services/userService';

export function useUsers() {
  const users = ref([]);
  const isLoading = ref(true);
  const error = ref(null);

  const fetchUsers = async () => {
    try {
      const response = await userService.getUsers();
      users.value = response.data;
    } catch (err) {
      error.value = err.message;
    } finally {
      isLoading.value = false;
    }
  };

  onMounted(fetchUsers);

  return {
    users,
    isLoading,
    error
  };
}

In the code snippet above, the userService is imported from a separate service file responsible for handling API calls. This separation helps in keeping the API logic distinct from the component logic.

The fetchUsers function is asynchronous and is used to retrieve user data. It returns an object that includes users, isLoading, and error. These reactive references can be utilized in the consuming component to display the data, loading state, and error messages.

Usage of useUser composable in component

<template>
  <div>
    <h1>User List</h1>
    <p v-if="isLoading">Loading...</p>
    <ul v-else-if="users.length">
      <li v-for="user in users" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
    <p v-else>No users found.</p>
    <p v-if="error">{{ error }}</p>
  </div>
</template>

<script setup>

import { useUsers } from '@/composables/useUsers';

const { users, isLoading, error } = useUsers();

</script>

This approach reuses the data fetching logic, keeps the component code clean, and focuses on the presentation logic. Always handle loading and error states to provide a better user experience. This can be managed using reactive properties.

Avoid Fetching Data in Multiple Components

Fetching the same data in multiple components can lead to various issues such as redundant network requests, inconsistent data states, and increased complexity in your codebase. To address these issues, it is best to fetch data centrally and share it across components as needed. Here, we'll explore several strategies to avoid fetching data in multiple components.

Strategies to Centralize Data Fetching

  1. Parent-Child Data Flow
    One simple strategy is to fetch data in a parent component and then pass it down to child components via props. This ensures that data is fetched only once and is shared across the necessary components.

  2. Provide/Inject API

    Vue's provide and inject functions allow you to share data between ancestor and descendant components without passing props through every intermediate component.

  3. State management

    For larger applications, using state management to manage the state centrally is a more robust solution. Vuex/Pinia provides a global state that can be accessed by any component, avoiding multiple fetches.

Using Pinia for State Management in Vue.js

Pinia is a store library for Vue, it allows you to share a state across components/pages. Pinia is the official state management library for Vue.js and is designed to be the successor to Vuex. It provides a more intuitive API and integrates seamlessly with Vue 3.

For more visit the official Pinia site. https://pinia.vuejs.org/introduction.html

Conclusion

The following are commonly used patterns by Vue developers to fetch data. By using services, composable functions, and state management for reusable logic, we can avoid fetching data multiple times from components. This helps to maintain code quality, efficiency, and readability and makes it easier to trace any bugs we might encounter in the future.

Thanks..

References

https://vuejs.org/guide/reusability/composables.html#composables

https://pinia.vuejs.org/

https://axios-http.com/docs/intro

https://learnvue.co/articles/vue-best-practices

1
Subscribe to my newsletter

Read articles from Binay Maharjan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Binay Maharjan
Binay Maharjan

๐Ÿ‘‹ Hello World! I'm Binay Maharjan, a passionate Front-End Developer the ever-evolving realm of web development. ๐Ÿš€ ๐ŸŒˆ Design Enthusiast: With an eye for aesthetics, I bring designs to life, ensuring a seamless fusion of form and function. My CSS skills extend to animations, transitions, and the art of making websites not just functional, but delightful. ๐Ÿ“ Responsive Design Advocate: From desktops to tablets and smartphones, I'm dedicated to creating websites that adapt flawlessly to every screen size. A user-centric approach guides my responsive design philosophy. โš™๏ธ Tech Innovator: I thrive on staying up-to-date with the latest trends and emerging technologies in the front-end development landscape. Constantly refining my skills to implement cutting-edge solutions, I am committed to delivering high-quality, forward-thinking code.