Data fetching

One of Nuxt 3 key features is the ability to seamlessly integrate data from various sources, making it easy to handle asynchronous data fetching and provide a consistent API for working with data.

Setting up a reusable data integration structure via the composable folder.

The composables folder is used to store reusable functions in a Nuxt 3 project. These functions provide shared logic that can be accessed globally without needing to import them manually in every file.

The process of integrating data into a Nuxt 3 application using a custom composable approach involves handling errors, managing endpoints and API requests.

Example Structure
// composables/api.js

import { ref } from 'vue'
import { useUserDetailsStore } from '@/store/userDetailsStore'
import { storeToRefs, createPinia } from 'pinia'

const pinia = createPinia();
const userDetailsStore = useUserDetailsStore(pinia);
const { token } = storeToRefs(userDetailsStore);

export function useApiRequest() {
  const runtimeConfig = useRuntimeConfig()
  const baseURL = runtimeConfig.public.BASE_URL

  const sendRequest = async (params) => {
    const {
      method = 'GET',  // HTTP method, default to 'GET'
      endpoint,        // API endpoint
      data = null,     // Data for POST, PUT, PATCH requests
      queryParams = {},// Query parameters
      customOptions = {}, // Additional options (e.g., headers)
      id = null        // Optional ID to append to the URL
    } = params;

    const { handleError } = useErrorHandler()
    const queryString = new URLSearchParams(queryParams).toString()
    let url;
    if (endpoints[endpoint]) {
      url =
        baseURL + endpoints[endpoint] + (id ? `/${id}` : '') + (queryString ? `?${queryString}` : "");
    } else {
      url = baseURL + endpoint + (queryString ? `?${queryString}` : "");
    }

    const headers = {
      Authorization: `Bearer ${token.value}`,
      'Content-Type': 'application/json',
      ...customOptions.headers,
    }

    const requestOptions = {
      method,
      headers,
      ...customOptions,
    }
  // If it's not a GET request and data exists
  if (method !== 'GET' && data) {
    // Check if data is an instance of FormData
    if (data instanceof FormData) {
      // Let the browser automatically handle Content-Type for FormData
      delete requestOptions.headers['Content-Type'];
      requestOptions.body = data;
    } else {
      // If it's not FormData, handle it as JSON
      requestOptions.body = JSON.stringify(data);
    }
  }

    try {
      const response = await useFetch(url, requestOptions)

      if (response.error.value) {

        throw response.error.value.data
      }
      return response.data.value
    } catch (err) {
      await handleError(err)
      throw err
    }
  }

  return { sendRequest }
}
Functions
  • useApiRequest: Initializes the API request function.
    • Parameters:
      • params: An object containing the request details.
      • method (string): HTTP method (default is 'GET').
      • endpoint (string): API endpoint.
      • data (object|null): Data for POST, PUT, PATCH requests.
      • queryParams (object): Query parameters.
      • customOptions (object): Additional options (e.g., headers).
      • id (string|null): Optional ID to append to the URL.
    • Returns: An object containing the sendRequest function.
  • sendRequest: Sends an API request based on the provided parameters.
    • Parameters:
    • Returns: The response data.
    • Throws: An error if the request fails.
Example Usage Structure
import { ref } from 'vue'

const { handleError } = useErrorHandler();
const { sendRequest } = useApiRequest();
const errors = ref({});

const useApiStructure = async () => {
  isLoading.value = true;

  try {
    const Data = {};
    const queryParams = {};

    const Params = {
        method: "",
        endpoint: "",
        data: ,
        queryParams: queryParams
      }
    const { data } = await sendRequest(Params);
  } catch (err) {
    handleError(err); // Call the error handler
  } finally {
    isLoading.value = false; // Stop loading
  }
};