As teams are moving away from the monolithic approach, decoupled architecture has become massively popular among developers around the world. However, what are the reasons behind the shift? Can you decouple easily using local-only fields in Apollo Client? In this post, you will find all the details.
What is Apollo Client?
Apollo Client is a comprehensive state management library for JavaScript. It allows you to build UI components conveniently. Using GraphQL, you can manage local and remote data, as well as easily fetch, cache and modify application data.
You can also cache and modify data while updating the user interface automatically. It supports easy integration with a variety of frameworks, including React and Angular. As a result, all the more developers are opting for it.
Why should you use Apollo Client?
- Universally Compatible: it works with all GraphQL servers and schemas and you can use it with any build setup.
- Easy to Use: you can get started with Apollo Client quickly by loading data right away. At this initial stage, there is no need to learn advanced features.
- Building Interactive Apps Made Easy: it helps you to build interactive web apps conveniently, allowing you to send queries and mutations synchronously. If the user makes a change, it will be reflected in the user interface instantly.
- Small and Flexible: with the core under 25kb compressed, it enables you to get rid of unnecessary stuff.
- Support for React: Apollo Client has been designed for React, thus you can utilize the latest features of the framework — including Hooks — to build the UI of your app conveniently.
Why decoupling frontend and backend development?
Traditionally, developers used a single codebase to build an app with complex functionalities. From connecting to databases to outputting simple text, everything was done in a robust codebase. There was no separation between the backend logic and the rendering of the site’s frontend. This approach is known as monolithic.
While monoliths were easy to set up, they became very problematic to scale and maintain quickly. To scale a single component or functionality of the app, the developers had to essentially scale up the entire application, making their day-to-day work very difficult and prone to errors.
To solve this issue, developers started to use decoupled architecture while building complex applications to separate the backend code from the frontend code. While backend and frontend have their own server instances, they can still fully communicate with each other.
By decoupling frontend development and backend development, you can build flexibility into your digital product or service, scale individual components independently and make rapid changes easily.
For example, one common use case would be trying to solve missing parts of the API that hasn’t been implemented yet by the backend developers, while the frontend developers need them for presentation reasons. Using Local Only the frontend developers can inject & fill the missing parts of the API in order to achieve their presentation needs, while it is still in development.
With this method the disruptions in your development stream are minimized, as frontend and backend developers do not block each other. This makes the working day of any developer and technology teams a lot easier, as they can now build different components using different languages or technologies and deploy them separately.
How to Use Local-only Fields in Apollo Client
Let’s think about a scenario where you are working on an e-commerce web application. Most of the information about the products is stored on your backend server. However, you would like to define a Product.isInCart boolean field that’s local to the client to help you to determine whether the product is in the cart or not.
You need to create a field policy for isInCart. It will enable you to specify custom logic for how a single GraphQL field is fetched from and written to your Apollo Client cache.
Defining
To define a field policy for Product.isInCart, you need to follow these steps:
1. Create a constant, called cache. Then create a constructor, called InMemoryCache.
const cache = new InMemoryCache({
2. Now, you have to add typePolicies. Then you have to include the policy map for the Product type. Next, you need to add the field policy for the isInCart.
typePolicies: { // Type policy map
Product: {
fields: { // Field policy map for the Product type
isInCart: { // Field policy for the isInCart field
3. Then you have to add the read() function. It will help you to return the queried product’s ID in the CART array in localStorage.
read(_, { variables }) { // The read function for the isInCart field
return localStorage.getItem('CART').includes(
variables.productId
);
}
}
}
}
}
});
Overall, the code will look like this:
const cache = new InMemoryCache({
typePolicies: { // Type policy map
Product: {
fields: { // Field policy map for the Product type
isInCart: { // Field policy for the isInCart field
read(_, { variables }) { // The read function for the isInCart field
return localStorage.getItem('CART').includes(
variables.productId
);
}
}
}
}
}
});
Querying
Once you are done with defining a field policy for isInCart, you can include the field in a query that also queries your backend server.
To create the query, you have to follow these steps:
1. Create a constant, called GET_PRODUCT_DETAILS. Then create a GraphQL query for getting the product details.
const GET_PRODUCT_DETAILS = gql`
query ProductDetails($productId: ID!) {
2. Then you have to add these lines:
product(id: $productId) {
name
price
isInCart @client
}
}
`;
The @client directive tells Apollo Client that isInCart is a local-only field. As a result, Apollo Client eliminates it from the query it sends the product name and price to your server for fetching. You then get the final query result only after all local and remote fields are populated successfully.
Storing
You can use Apollo Client’s reactive variables to store the local state. You can read and modify them anywhere in your application without using GraphQL operation.
To store local state using reactive variables in the e-commerce web application, you have to go through these steps:
1. If you want to fetch a list of the product IDs in a user’s cart, and the list is stored locally, you have to make a query by using these lines in the Cart.js file:
export const GET_CART_ITEMS = gql`
query GetCartItems {
cartItems @client
}
`;
2. To initialize a reactive variable that stores the local list of cart items, you have to use the makeVar function in the cache.js file.
import { makeVar } from '@apollo/client';
export const cartItemsVar = makeVar([]);
3. Now, you need to define the field policy for cartItems by using these lines in the cache.js file.
export const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
cartItems: {
read() {
return cartItemsVar();
}
}
}
}
}
});
Whenever cartItems is queried, the read() function returns the value of the reactive variable, which is cartItemsVar.
4. Next, you have to create a button component that enables the user to add a product to their cart. Add this code to the AddToCartButton.js file.
import { cartItemsVar } from './cache';
// ... other imports
export function AddToCartButton({ productId }) {
return (
<div class="add-to-cart-button">
<Button onClick={() => cartItemsVar([...cartItemsVar(), productId])}>
Add to Cart
</Button>
</div>
);
}
Clicking the button will update the value of cartItemsVar to append the associated productid.
Wrapping Up
By decoupling frontend and backend development, you can enjoy greater flexibility and scalability and make rapid changes without disrupting the entire application. By using Apollo Client, you can include local-only fields that are not defined in the schema of your GraphQL server, while each component remains completely unaware of each other.
As autonomy is a key principle of our engineering culture in Dialectica, we are very fond of using Apollo with Local-Only fields to decouple our frontend and backend development streams. This way we empower our teams to be autonomous and be able to progress towards the same goals without blocking each other.
Our technology team is hiring now! Visit our dedicate Technology Careers page to explore our Software Engineer job vacancies. Apply today for our Full Stack, Front-end Developer, Back-end Developer jobs in Athens to join #teamDialectica.