Finch GraphQL - a chatty bird.
The ugly side of extensions.
Extensions are different. I think the type of developer that would be creating an extension is a web developer. A website and an extension behave quite differently. For websites, we have technologies like web workers and service workers, but these technologies are additive. You would add a service worker to heavily cache your site, but it’s not needed for every site. In extension development, you will probably depend heavily on a background script, which can be thought of as a service worker. In Chrome, they are actually service workers in manifest v3, the latest version of extensions.
Need to make an API call? It’s in the background script. Need to track some analytics? It’s in the background script. You can quickly see that if you have an extension, you’re going to need a background script and you will need to communicate between your client script and this background script. Sadly the technology is not much different to communicate between a service worker and a background script. This can become a nightmare when working in a larger extension like Toucan which has tons of messages.
For example, let’s say I want to send three messages and wait for a response, then handle errors and show a loading state for all this. Two of these messages have zero error handling. If there are not any conventions set in place early on this can be an absolute mess. If you imagine having a nice responsive UI, you can see how this quickly can be like herding cats. These issues were catalysts to a larger idea around extension communication, and some conventions to build a modern extension.
An idea and spark of joy.
I had a few open questions while trying to untangle this messaging problem. What if we made a convention to make this communication easier? What if the communication looked like I was just making an API call? What would it take to make extension code looks more like the code of a website. Could I leverage existing technologies that my team is comfortable with to achieve this? All these questions floated around my head and it reminded me of another library I have used in the past.
I was fascinated with Gatsby for a good period of time. It’s a framework for generating static sites, and it leverages GraphQL. Gatsby essentially has a GraphQL API to your local file system which plugins are able to extend GraphQL. At first, it’s strange to think how it all works, but after using it for a while you get why it’s built that way. It feels very comfortable to pull in the content of a local file and place it into a template, almost like it’s just like hitting an API and pulling that info into a template.
When dealing with this extension communication I thought, why can’t I use GraphQL for this? Is it possible? Would this be comfortable to use or just feel strange? To answer these questions, I would need a proof of concept.
Putting ink to paper
I remember starting this document to plan out how to tackle this, and to be honest if it’s possible.
I had some experience digging into Apollo server code and knew they used a library called @graphql-tools/schema which essentially allows you to take a schema and set of resolvers and make queries against it. If I was able to install and use this in the extension I should already be super close to proving that this would work.
I remember the first couple of bits of schema were not revolutionary but seeing the logs of being able to pull the information in the background script felt great. Quickly I was able to wire it up to the messaging system in the extension. I threw that same query through a message and was amazed at how easy it was to get it to work! This was the start of the project we now can Finch GraphQL.
The initial reasoning behind build this graphql tech was around making it possible to execute multiple resolvers and grab larger amounts of data with less back and forth, but after playing around with this there were quite a few other things that I was immediately able to benefit from.
Errors in the background script can be painful for an extension. Does this error crash the entire process? If I am processing multiple messages does it just drops all the additional messages? It does and these can be pretty fatal. Essentially everything stops working. Introducing this GraphQL layer essentially got rid of random message crashes because GraphQL bubbles errors from resolvers. What once was a crash now is a handled error message. Even errors when user profile data in Chrome is corrupted, we can actually surface that information to the user to let them know what’s going on. This was amazing for stability but was not the only benefit.
Before this project, we used to duplicate code lots of code, and messages because the client needed something slightly different every time. A lot of times the existing contract was incompatible and we would either break the contract or duplicate code. The latter is the safer version and would often happen. Resolvers by nature are reusable and have a nice graph that essentially allows the client to define what it wants and we constantly just reuse the graph in different forms.
One more thing that is amazing about the existing infrastructure for GraphQL is that it is pretty well built out, just look at Graphiql and Apollo playground. I made some tiny tweaks to Graphiql. I was able to have full introspection of our graph, set up queries straight, and debug issues with queries directly in this GraphiQL. After getting these tools set up, I really felt the power of Finch.
Six-month retro with Finch.
After 6 months, I feel more in love with Finch GraphQL than ever. I am always wondering what is the next step with Finch. How can we make it better, faster, stronger? I feel most things are amazing. The amount of visibility we have into our application is amazing but there are a few things that I feel would take it to the next level.
We have a dev tool extension, that you can use with your extension. It’s only for Chrome, but it gives you insight into what Finch GraphQL is up to. I think these tools are pretty immature but are getting better daily. These types of tools are key to the adoption and usefulness of Finch GraphQL. Things like message inspection need some work and over time I think Finch GraphQL will adapt and these tools will get even better.
Another area of growth is with GraphQL Subscriptions. I often find we are mutating then re-fetching it would be awesome to get subscriptions working and be able to auto-update these queries. This should not be terrible to do since everything is local. That being said, it’s probably a lot of rewriting of the underlying Finch bindings.
Another potential upgrade would be more robust caching. We just recently released our first version of caching. This could lower the number of queries that we refresh, and also help manage the amount of memory we use in scripts like content scripts.