I’m interested in getting feedback on the architecture of a web app I’ve inherited. Some useful context:
- App is client-side rendered, written in React and uses Apollo GraphQL for data fetching. SSR is out of scope and adds more complexity than can be justified for an admin tool.
- App has a few main top-level routes. The entry point to the app is a dashboard listing a huge number of "projects". Each project has a dedicated page (let’s call it the Project Detail Page or PDP). A project consists of a bunch of project details/metadata, several assets, and posts that consume those assets.
- Using the PDP as an example, the page makes tons of small GraphQL queries (around 30 for an average page). There’s a query to get all the project metadata. For each asset, there is one query to get the asset details and another to resolve its thumbnail. And for each post, there are also two queries.
We’re interested in improving the performance of this application. To that end, I’m curious what the current best practices are data fetching architecture here. I’ve formed an opinion on this (described below) and would like feedback on if this is a good approach or not.
Proposed Architecture
- Having many small
useQuery
s distributed around the app is convenient for getting access to the relevant data in the places that care about that data. So, to that end, the current approach works nicely. I’d like to avoid implementing large scale state management (like Redux) as I think it complicates things and adds an unnecessary intermediate layer a lot of the time. - However, having many small queries adds a lot of network overhead and introduces a lot of layout shift as different parts of the page resolve at different times.
- My proposal would be to create a single, larger GraphQL query for each top-level page. This query would resolve the project details which includes asset IDs and post IDs, which would then be resolved into hydrated asset/post objects too before that data is returned in one payload. That way, all the granular
useQuery
s distributed around the app could remain in place and would basically be hitting the Apollo cache instead of the network. In a nutshell, using one big query to hydrate the cache and using it as a state management solution almost.
Does this approach make sense? Am I not thinking of something?
2
Answers
One of the strengths of GraphQL is the ability to get exactly the data that you use in the UI, fetching this data from the backend in one query. It is also a good practice to keep representational components free from side effects.
So, your idea to fetch more data in fewer queries makes total sense.
There is one notable exception. Let’s say that along with your project you need to fetch a list of posts that you want to receive and display page by page. In this case, it might make sense to fetch this list in a separate query to make it easier to implement pagination
Another things to consider:
@defer
directive to load parts of your query that is less important for user and take long to load. But it should be supported by your GraphQL backend, and as far as i know only GraphOS/GraphRouter currently suports it (apollo-server v4 just ignore it).Just a couple of points to complement Slava’s already very accurate answer:
@client
directive, you can add local-only fields to your queries, making Apollo an advantageous solution for global state management.