When you are learning about a new frontend library, such as Vue or React you can read up a lot on certain best practices. There should be no problem finding resources on effective component composition, how to prevent performance bottlenecks, and the basics of state management, such as Redux, VueX, and so on.

But then, there’s an area that is hard to find any information for and that’s how to build a good data layer. It’s common to put a lot of care to optimize and organize the view layer but the data layer might get neglected and becomes a frequent source of bugs.

What is a data layer?

By a data layer, in the context of frontend applications, I mean actually three layers:

  1. Fetch layer — code that contacts the server, using XHR, Fetch or Websockets. Usually a set of services (classes) or pure functions.
  2. Normalization / Serialization layer — code that processes incoming and outgoing data.
  3. Store layer — code that handles saving and accessing data to a client-side store.

There’s surprisingly little information online on how to do these well. Maybe it’s because this logic is highly dependent on the design of your API and it’s hard to generalize. Yet still, I’ll try to provide some guidance.

Do you even need a data layer?

If a component directly does an ajax call and uses the raw response to render the data without storing it in any kind of client-side store that essentially means there’s no data layer involved.

In some cases that is fine, in a lot of cases not at all.

With this approach, problems might arise when…

  1. The same kind of request is being done in multiple places and the fetching logic needs to be reused
  2. Data coming from the server need to be processed.
  3. Some or all of the data might need to be reused elsewhere
  4. Data need to be used again, for example when the user goes back to the previous route and triggering the same request again would not be optimal.
  5. There’s SSR involved and server state needs to be passed to the client

As more and more of these need to be covered, the complexity can grow exponentially. Handling these issues directly in a component can easily grow out of hand and so you have to start moving the logic elsewhere and start generalizing it — and that’s how the data layer slowly starts to form.

If your backend supports a conventional solution such as GraphQL or complies to a standard like JSON:API, you might save the majority or all of the work by using an existing data layer client, like Apollo or vuex-orm. Apollo requires a GraphQL backend, vuex-orm makes sense to use especially if your API is Restful.

If not and your backend is unconventional, the job is on you. That’s the case I’m gonna cover.

Fetch layer

Let’s start with a component that does an ajax request with Axios and passes the data to the template.

The component fetches and processes the result:

	export default defineComponent({
	  setup() {
	    const data = ref(null);
	    const error = ref(null);
	    const isLoading = ref(false);
	    const fetchUsers = async () => {
	      try {
	       isLoading.value = true;
	       const response = await axios('/api/users');
	       data.value = response.data.data.map(user => ({
	         ...user,
	         createdAt: new Date(user.createdAt)
	       }));
	      } catch (e) {
	       error.value = e;
	      } finally {
	       isLoading.value = false;
	      }
	    };

	   fetchUsers();
	   return { data, error, isLoading, fetchUsers }; 
	  }
	});

This is a very low-level approach to contacting an API directly in a component. We see a bunch of refs being used such as data error isLoading . We pass those to the template together withfetchUsers function itself — so that it can be triggered again if needed (letting user to retry for example).

Still, it’s a lot of code just to get some users and surely logic like this will be done at many places in the code.

#javascript #vuejs #programming #web-development #vue #composition api

Building a data layer with Vue and Composition API
1.75 GEEK