Home > OS >  Vue3 - How to avoid repeated code in API calls?
Vue3 - How to avoid repeated code in API calls?

Time:08-13

I have a Vue3 app in which I use the following approach to API calls:

  • there's an api folder, inside which lie functions that directly make a call to the backend. For example:
export async function getCourses(): Promise<Course[]> {
    const response = await axios.get(`/courses/`);
    return response.data;
}

export async function getCourse(courseId: string): Promise<Course> {
    const response = await axios.get(`/courses/${courseId}/`);
    return response.data
}

export async function getExercises(courseId: string): Promise<Exercise[]> {
    const response = await axios.get(`/courses/${courseId}/exercises/`);
    return response.data
}
  • there a Vuex store, whose actions invoke those functions in the api folders and commit data to the store's state

  • Vue components access the store state and dispatch actions to it in order to retrieve data

The problem I have with this approach is that the api functions get extremely repetitive. I have to define 4 different functions just to enable CRUD on an entity, and all of them take almost the same parameters (for example, in my app, all resources are sub resource of a course and hence require the courseId parameter for fetching) and have the same structure (await an axios call and return the response's data).

I'd much prefer a method where I declaratively define the routes and the parameters they expect (as far as url components such as id's), and then be able to type-safely do something like this:

getApiService().courses(courseId).delete() // deletes a course
getApiService().courses(courseId).exercises().get(exerciseId) // returns an exercise
getApiService().courses(courseId).exercises().post(payload) // creates and returns an exercise
getApiService().courses(courseId).exercises().get(exerciseId).choices().list() // returns the list of choices for an exercise
// and so on

What would be a good way to accomplish this? Are there any existing packages that do this for Vue3?

CodePudding user response:

You're wandering into the middle ground of wanting to slightly improve the interface of a worldwide favourite industry standard library. Don't do it ! It's so fraught with pitfalls. Your desired interface has your concepts in the code (courses) so only you are going to write it. But the thing you write will be flaky and incomplete, and will get in your way as soon as you need to handle an error condition or add a custom header. Longer I live, more I think everyone's project should be one layer thick. Search your conscience as soon as you start calling your own code. Use these fabulous libraries, vue, axios etc, and marvel at how much they're doing for you and how terse their interfaces are. It was much worse before. A little bit of repetition is worth it for the sake of simplicity, clarity, ease of maintenance.

CodePudding user response:

The current code is clean and reasonably WET, this makes this much easier to maintain than possible DRY solutions. The hazards of zealous DRY approaches are covered in this popular talk and explained in this article.

If it's known these functions always return untransformed data, they could be replaced with some DRYer helper function:

const apiCall = <T extends any>(url: string, options?: AxiosRequestConfig<T>): Promise<T> => {
  const response = await axios<T>(url, options);
  return response.data
}

Passing HTTP method through options makes it more flexible to use.

  • Related