Powering Streak’s Gmail Add-on: Part 1 — Backing into GraphQL

Fred Wulff
Dec 27, 2017 · 5 min read

At Streak, we recently partnered with Google to release our Gmail Add-on as part of the Add-ons launch. The Add-on is our first major foray into integrating with your existing mobile apps: it lets you better organize your work email in the mobile Gmail app, and makes it easier to collaborate with your team to make sure nothing slips through the cracks. The reception has been great. Our Add-on is top rated in the Marketplace and tens of thousands of users interact with it regularly. But the backend of the Add-on is also new: it’s 100% powered by GraphQL, albeit in an unusual way, and I wanted to share the engineering story of how we got here.

The Streak Gmail Add-On on Android

A Performance Problem

Our GraphQL journey started with us staring at a loading spinner. The fundamental purpose of our Gmail Add-on is to give the user more context about their emails. Useful context comes in many forms. If you’ve told Streak that an email is part of a sales deal, then we should add information about that deal when you view the email. If the customer you’re talking to is also talking to somebody else on your support team about an issue, you probably want to know that. And if the customer’s trial is going to run out today and might need to be extended, that’s important, too.

Surfacing all this context is the core of our user experience, but the information lives in a lot of different places, so displaying it requires querying a bunch of different API endpoints: pipelines, contacts, deals, other emails, and organizations just to start. Our main Javascript client pipelines these queries using parallel, asynchronous requests against our REST API, making sure that we’re pulling contacts at the same time as organizations at the same time as the deal information.

Some of the many parallel requests fetching one box using the web client

This was where we hit our first Add-ons roadblock. Google Apps Script, which powers Add-ons, is a hosted Javascript sandbox environment with some non-standard APIs. Instead of the traditional XHR interface, requests to backends are required to use a custom URLFetch service. And URLFetch only allows blocking requests. Requesting all the data serially could take tens of seconds. Ain’t nobody got time for that.

We realized that to get acceptable performance, we needed to combine the requests. Instead of getting “/email/15fb6cb3b627304f” followed by getting “/contacts/15fb6cb3b627304f”, we needed one endpoint: “/emails+contacts/15fb6cb3b627304f”. We spent a while manually bundling handlers together, but it turns out nobody on our team wanted to pick up artisanally hand-crafting batch endpoints as a long-term hobby.

Griping about it at lunch one day, one of our frontend developers suggested GraphQL. GraphQL bundles a lot of functionality that’s useful for making flexible APIs in a query language and a simple HTTP protocol. And relevant to our interests, GraphQL lets you request multiple objects in one go.

A single GraphQL query fetching multiple objects

It also provides functionality for introspecting response schemas, only fetching portions of a response (to save bandwidth costs), and for eagerly requesting related objects (e.g. following foreign keys to their object). This is very powerful, but in the context of a user-facing API, also a little scary. Some of our teams have hundreds of thousands of deals. What if somebody requested all of them and their related information in one go?

So we decided to take a measured approach to GraphQL, using it only for communication between our Add-on running in the Apps Script sandbox and our backend to start.

GraphQL Implementation Strategies

There are two main models for retrofitting GraphQL onto a RESTful API backend. The first adds a proxy layer in front of the existing server, typically in Node since that runtime has the original GraphQL backend server. The proxy translates the GraphQL query into traditional API requests, sends them in parallel, and constructs the GraphQL response.

The second model adds a GraphQL endpoint directly on our backend server, using the graphql-java library. This endpoint either programmatically requests information from the existing endpoints, or does the same work they do to fetch the data from our backend datastore.

The main arguments in favor of the proxy approach was that it was completely separated from our existing stack. Our existing endpoints were well-tested, we were confident that we knew how to monitor them, and we knew that they correctly enforced permissions.

The main arguments for the integrated approach center around development and runtime efficiency:

Initial Results

Since our whole purpose of using GraphQL was to make our Add-ons more efficient, we decided to pursue the integrated option. After some fun with Maven and getting Java 8 deployed on App Engine, we got a proof of concept working.

The good news: requests were batched and the request took about half as long as the previous serial requests.

The bad news: half as long as the previous serial requests was still roughly six seconds longer than we were looking for.

Hopeful but with a ways to go, we dove into the exciting world of GraphQL layering and instrumentation to track down the lingering performance issues.

We’ll talk more about our GraphQL experience next week, and in future blog posts delve into the build infrastructure we developed to build the Add-on itself and how we used Google Cloud Spanner to enhance our backend performance.

Streak Engineering Blog

A blog documenting the technical journey of Streak

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store