Skip to main content

Annamalai

GraphQL is a new technology and it completely brought in new ideas on how to communicate better between client and server. The benefits, when compared to REST APIs, are fewer network requests, colocate data requirements, better tooling and documentation like strict typing, automatic update of documentation when the schemas are changed, etc.., Though it brought in multiple benefits, it does suffer from certain issues. One of the issues that I am going to explain in this post is the normalizing of responses. For example, let's say there are 5 people in a social network and they are pretty close and have a lot of mutual friends in common. So a typical to fetch the person's details and their friend's details using graphql query would be like something as shown below,

{
person {
id
email
friends {
id
email
phone
company
name
about
age
gender
favoriteFruit
}
}
}

Now inspecting the response returned by the graphql endpoint, we notice that there a lot of 'Friend' responses repeated because as said, they have a lot of mutual friends. The size of the response is around 350 KB in size and it is quite a lot. There is no such idiomatic way to reduce the size of the response in this scenario. Now let's rewind back and see, what it would be the case in the REST API scenario. In REST API world, there are lots of established response formats like JSON:API, HAL, ODATA, etc.., In case of JSON:API, it does have a solution to these kinds of problems by normalizing the response into grouped entities to reduce the size of the response. It does so by grouping into entities and adds a relationship to the entity as a reference in each repeated place. After reading a few blog posts, I arrived with this solution by using the gajus's GraphQL Deduplicator library to deflate and inflate responses to bring down the size.

Why use Cloudflare/Service Workers?

The JavaScript community has the best tooling/libraries support for GraphQL and thought of taking advantage of it. Here, the GraphQL server could be written in any programming language like Ruby, Python, Scala, Rust or whatever you wish to use and in order to use these JavaScript GraphQL libraries to implement or experiment these kinds of ideas, either one has to convert these JavaScript libraries to their desired language and it is time-consuming and a waste of resources when we are just trying to experiment. Overall the benefits I see in this approach is,

  • Take advantage of the JavaScript ecosystem even though the graphql server is written in a different language.
  • No need to change a single line of code in the backend server for this experiment.
  • In the case of frontend, just a piece of code to the service worker to inflate the responses.
  • It helps to quickly prototype to check if it brings any new performance benefits or not.

Cloudflare Worker

This place acts as an interceptor between your client and server communication so we use the graphql-deduplicator library to deflate responses return by the graphql endpoint. We do a bunch of checks to make sure it is the graphql endpoint URL and we deflate the responses obtained from the upstream server and then, we send a newly constructed response to the client. To see a demo of how the deflated response looks like,

You can visit https://graphql-compressor.asvny.now.sh/ and click 'Fetch Deflated' button'.

As you scroll down, you can notice that some 'Friend' responses have only __typename and id because the response was normalized and these two attributes act a reference to the resource. The original content can be seen only once in the deflated response.

Service Worker

Since, GraphQL is generally a "What you see, is what you get", we have to inflate the response returned by the Cloudflare worker back to its original form to avoid errors. The same checks are done again to know that we are intercepting the correct graphQL endpoint and then we inflate to its original form.

You can visit https://graphql-compressor.asvny.now.sh/ and click 'Fetch Normal' button'.

Results

For a sample of 5 persons with 20-25 mutual friends, the original response was 280-320 KB in size and after introducing the above technique, we can notice an unbelievable decrease in size and the size was around 70-90 KB. All the sizes that I have mentioned are actually uncompressed sizes if we are talking with gzip, br encoding then the difference was very marginal. With encoding, the difference was around 0.5 KB to 2 KB.

Conclusion

This was just more of an experiment on how we can take advantage of JavaScript libraries in a graphql server written in another language. It helped me to realize how much powerful it is when we simply turn on compression in the servers (like gzip or br).

To see the full code, here is the github repo link.

Thank you, reader!