Why we are moving off REST and implementing GraphQL
Seth Carney
Posted on November 24, 2020
Why we decided to transition to GraphQL
When we started building Courier, we investigated GraphQL, but the options for running a serverless version of Apollo (the technology we wanted to use) were limited and less stable. Because we don’t run EC2 or have dedicated servers, that was a major consideration for us. However, that’s changed quite substantially since we first looked at Apollo. Since then, we’ve been able to start transitioning both our internal and external APIs to GraphQL.
I’ll explain the reasoning behind this below.
Limitations of using REST
REST has been around for a long time, and today it’s still the most widely-accepted way to write an API. REST is a specification that sits on top of HTTP. API calls are structured around objects (like profiles, preferences, and templates) using dedicated HTTP endpoints. For example, if you wanted to expose a way to programmatically manipulate your user profiles, you might have a REST endpoint /user/{userId}
which can be queried to perform CRUD operations using HTTP GET
, POST
, DELETE
, etc. Writing a REST API is pretty straightforward – but REST can be tricky to use as an API consumer.
First, REST wasn’t designed for complex sequences of operations that don’t fit neatly into the CRUD bucket. It’s not easy to update two objects at the same time, for example, and even retrieving data in certain scenarios can require multiple calls and branching logic as one endpoint might have to call another one. Another downside of REST is it puts a lot of responsibility on the API consumer (which may be your internal developers or your customers) to know how the underlying data is structured. That’s not optimal for several reasons.
The API calls aren’t oriented to the common actions that the user wants to take. They’re structured rigidly around your objects. That means someone might have to call the same REST endpoint to set a label and add a collaborator, even though these are two completely different use cases. Another reason it’s not a good idea to structure your API around how your data is organized is because things change. Changes to your data are inevitable and it’s hard to adapt REST APIs to these changes (if you do find yourself in this situation, here's how we approached standardizing our REST API).
Advantages of moving to GraphQL
GraphQL is a query language with a very developer-friendly approach to building APIs. It's based on the idea that the API consumer shouldn’t have to know anything about how the data is stored internally. Instead, you describe your data’s relational schema and the consumer can query that nested data from a single endpoint that never changes. GraphQL also conforms to the idea of CQRS, or command-query responsibility separation – put simply, it means the way that you query data is different from the way you mutate data.
One of the things I like best about GraphQL is, as a side effect of implementing it, you’re forced to live by some of those rules of software engineering that you really should be living by. You have to think about your data holistically and you don’t end up with a bunch of poorly-designed endpoints lying around as the result of shortcuts you took to meet deadlines.
Because of how it’s built, GraphQL is really good at versioning: you can mark functionality as deprecated and you can change the underlying infrastructure without breaking existing integrations (and without the consumer even knowing). GraphQL also has a solid caching layer, which reduces our total operational costs because we end up not hitting our database as much. Because we’re a serverless shop, we will actually be implementing our caching layer through ElastiCache.
Using GraphQL at Courier
How we decided which technology to use
As I mentioned earlier, we thoroughly researched the options for implementing GraphQL and kept an eye on possible solutions. There were two main options that emerged for our use case: AWS AppSync and Apollo GraphQL.
We evaluated AppSync because we’re an AWS customer, we use cloud formations, and it was appealing to be able to stand something up quickly. But there were some core security choices we made when implementing multi-tenancy in Amazon Cognito that made the switch to AppSync difficult. We realized AppSync wasn’t going to work for us unless we changed some of those fundamental decisions.
But that wasn’t the only reason we decided to go with Apollo. Compared to AppSync, which uses the Apache Velocity Template Language (VTL), Apollo is just JavaScript. When we work with Apollo, we don’t have to do a lot of the mental context-switching that happens when you use different languages. Not to mention, Apollo is popular for a reason: it’s a rock-solid product that’s constantly evolving and has a growing and supportive community of users. Finally, we chose Apollo for the Apollo Federation, which will help us grow our Graph without affecting our performance as our product scales.
Our roadmap for transitioning to GraphQL
Right now, we’ve moved some of our internal APIs to GraphQL, such as the infrastructure for accessing users and tenants. We’re also building all new features with GraphQL as well.
While it'll be some time before we move all our internal APIs to GraphQL, we have plenty of important candidates for this transition. One key use case is autosave during template creation in our notifications designer. When you're editing a template, you can add content blocks like text or images, add notification channels like email or SMS, and add conditional logic (just to name a few examples) and, as soon as you make a change, it gets autosaved. Behind the scenes, these edits are funneled through a common processor.
One of the problems in REST is it’s difficult to do partial updates. The various components end up having to send the whole template resource when they want to update a single field. Sure, you can implement PATCH endpoints, but those come with their own complications. When you factor in doing validation on the full object with every call, autosave has the potential to become an expensive operation. Moving autosave operations to GraphQL mutations will help us solve this problem outside the constraints of a traditional REST API design and more closely represent the types of actions our users are taking.
As we move all our internal infrastructure to GraphQL, our ultimate goal is to expose a GraphQL interface to our customers, along with an explorer that will make it so consumers can interact with our schema right from the browser.
If you’re interested in working with GraphQL, we’re hiring engineers at Courier. You can check out our open roles here – we hope to hear from you!
Posted on November 24, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.