Shopify is an e-commerce company that makes it easier for people to sell and buy things via online stores. As with many other companies, Shopify developers experienced many of the pain points of using REST APIs, including making too many round trips to fetch data and having difficulties customizing the data that they received.
After experimenting with React Native, Shopify discovered GraphQL, and in September 2016 the company released a new version of its mobile app powered entirely by GraphQL. Here are some of the methodologies used and lessons learned during Shopify's GraphQL adoption.
Shopify's app is a monolithic Rails application, and developers initially used a Ruby gem for GraphQL that allowed them to execute queries and define things like types and field arguments using a domain-specific language. However, since they wanted to make it easier for other developers to extend their work, Shopify also built two abstractions on top of the existing gem.
The first abstraction, GraphModel, is a layer that helps define types, while the second abstraction, GraphApi, contains code that is more Shopify-specific. The team also created helper methods that mimic the syntax of Active Record, such as belongs_to and cache_has_many.
Shopify developers noticed that resolvers were getting called in different contexts, which meant that calls to the server were sometimes being made unnecessarily because different resolvers were unaware of other resolvers' behavior. To solve this problem, they used the standard solution of query batching: sending multiple queries to the server within a single request.
When using GraphQL, developers often encounter the pain point of performing authorization and authentication. Shopify realized that the access tokens for its API clients already acknowledged a scope in terms of the resources that were able to be accessed. They used this same concept for their type definitions: for example, an order type requires access to orders. Developers can also specify whether the client is allowed to write, or has only read-only access.
Shopify also built in protections against malicious client behavior in GraphQL. For example, to guard against clients who construct extremely complex queries, Shopify used Ruby's Timeout class, which raises an error after a certain amount of time has elapsed. In order to prevent a high quantity of queries, Shopify reused its solution for REST APIs, which was to throttle the client once the leaky bucket algorithm detects abnormal behavior.
Shopify put several initiatives in place to make the transition to GraphQL as easy as possible. The team's GitHub repositories use a bot that can detect when the schema is altered and help find changes that break the schema. Developers also use conventions such as prefixing mutation actions with the name of the object instead of the action. Finally, Shopify is able to track deprecated fields to know when they are executed and when it's safe to remove them.
When Shopify developers were rebuilding the mobile application, they knew that they needed to improve on the existing limited companion app and provide a full experience so that users could administrate their stores from their phones. They began by creating it in React Native, but this led to some problems with polish and quality. The team ended up using native SDKs for Android and iOS, which meant that they would need to build GraphQL clients in Java and Swift.
Shopify had several goals when building the new GraphQL clients. For one, the code needed be simple, avoiding unnecessary complexity, and also be easy to understand. Additionally, the schema needed to be able to evolve and accommodate additions over a long period of time, especially after release.
During development for the mobile app, the team created query builders that construct GraphQL queries. The query builders were written using the builder software design pattern, which constructs a complex object piece by piece. Fields are added by calling methods, which are chained by returning the object on which they're called, and closures are used to specify subfields on a particular field.
Developing these query builders in Java initially presented more of a challenge than in Swift, because the Android SDK lacked support for lambda expressions, which were present in Java 8. However, the team discovered the Retrolambda plugin, which allowed them to backport this feature to older versions of Java so that they could use closures in Java as well.
Finally, since they didn't want to work with raw JSON data, the Shopify team had to decide on the structure of the objects that the client receives in response to a query. These response objects are already validated when they're constructed and deserialize fields based on their type in the constructor.
Objects can work with unknown types in order to accommodate enum values that aren't known at the time of code generation. This use of unknown types also allows room for the schema to evolve in future versions of the application.
The Shopify engineering blog contains many articles about the company's technical stack, including GraphQL. In addition, the Shopify team is hoping to open-source some of the tools and code that it developed while working with GraphQL in the near future.