RTK Query
Decision
For client-dashboard-2, we start using RTK Query to interact with the API.
Problems
We are currently using different methods to access the backend services (backend api calls) throughout the application and there is no standard way to do it. These methods require us to manually write boilerplate code to handle loading state. This is additional work and leads to longer, harder to read code. Some methods used also do not cache requests to the backend correctly, leading to multiple requests from different components.
Context
We need to write a lot of code for API communication. Currently, we use an OpenAPI schema generator that outputs typed fetch calls to use in our code. The generated calls do not handle the API call's lifecycle.
We also recently standardized on inter-component communication.
Options
- Not using a library
- Tanstack/React Query
- SWR
- RTK Query
Reasoning
We decided to go for RTK query for multiple reasons:
- Have one standard way of doing API calls without writing boilerplate code. RTK Query gives us the possibility to use generated hooks that output the whole lifecycle of the API calls
- RTK Query provides a package that generates all hooks based on Open API schema @rtk-query/codegen-openapi. This means that for most cases we don't even need to write the endpoint setup
- The library caches the data that is retrieved from the API which improves both user and development experience. For users any subsequent calls to the same API will be fetched from cache making the whole experience faster when navigating through the app and for developers this enables writing components without any concern of sharing information between siblings and the code will be uniform
- RTK Query comes bundled with RTK library which we already use and are familiar with
- Even though it is not the most used library, it is still used by enough companies due to the usage of RTK
- Our OpenAPI schema already has
tagsdefined which will make cache invalidation work out of the box with generated hooks - Can improve UX easily using
optimisticUpdates
Consequences
How do we implement this change?
Initial setup has been done during Learning Friday by the team and can be viewed on this branch
Who will implement the change?
The Create team will configure this library to the client-dashboard-2 project
How do we teach this change?
Vlad held a Workshop about this library on a Learning Friday and also the team has done initial setup and configuration as an ensemble on another Learning Friday.
Learning curve is pretty lean as the API for the generated queries is not very big and the team can start using the generated hooks right away once configuration is done.
What could go wrong?
Although the team have used similar libraries (Apollo Client, Tanstack Query) and the concepts are the same, there is little knowledge in the team regarding the library and its more advanced usages.
While having cache baked in the library is convenient we often transform responses from the API to fit the frontend and will require some time to get used to cache manipulation.
What do we do if something goes wrong?
As this is only an abstraction over boilerplate code falling back to how we initially wrote API calls is always an option, although will take time. The good thing about this abstraction is that it makes us write our components based on the return values of the hooks which in general will be data, isLoading, isError that can be re-written to React state variables and handled in other ways. An example would be:
This RTK Query Example:
const { data, isLoading, isError } = useGetVisuals({ id: manualMailingId });
Abstracts:
const [isLoading, setisLoading] = useState(true);
const [error, setError] = useState<typeof Error>(null);
const [data, setData] = useState<GetVisuals>([]);
const fetchVisuals = async () => {
try {
const response = await campaigns.getSlimCampaignsIdVisuals({
id: manualMailingId,
});
setData(response);
setIsLoading(false);
} catch (e) {
setError(e);
errorHandler(ERRORS.PAGE_MAILINGS, e.status);
}
};
useEffect(() => {
(async () => {
await fetchVisuals();
})();
}, []);
Because other libraries have similar interfaces, it is easy to try other solutions.
React Query Usage Example:
const { isLoading, error, data, isFetching } = useQuery({...})
What is still unclear?
If using the RTK Query generated hooks will be able to replace all our use cases with interacting with the backend.