April 25, 2017

One GraphQL client for JavaScript, iOS, and Android

Jonas Helfer

Jonas Helfer

Earlier this month we launched Apollo Client 1.0, and it was super exciting: There were some great comments on the Hacker News thread, a ton of new downloads, and many new developers joined the Apollo community.

Our core idea — that managing data in your frontend should be simple — has really resonated with people. That’s what Apollo is all about. We’re working hard with the GraphQL community to make data loading and management straightforward, so you can focus on building a great app.

While Apollo Client for JavaScript is the most well-known of the Apollo projects, we also have two excellent, rapidly-growing native clients for iOS and Android. Just last week, we announced Apollo Android 0.3, the first release ready for public use!

Today, we want to tell you about one of the most exciting initiatives we’re working on in the coming months, and how we’re collaborating with teams at companies like Airbnb, New York Times, Shopify, and Coursera to make it happen: Unifying Apollo Client across all JavaScript and native platforms.

Read on to hear about why we think a cross-platform client is important, the use case for teams like Airbnb, and how you can get involved.

Everyone’s on mobile

Two big trends are currently happening in web application development:

  1. Mobile is now the dominant platform. In fact, phone and tablet usage surpassed desktop computer usage in 2016.
  2. React Native is becoming an increasingly popular choice among mobile developers due to its simplicity, cross-platform compatibility, and code push capabilities.

This means more and more companies are now building apps for at least three platforms: Web, iOS, and Android. Even though the different platforms conceptually represent just one application, they usually end up with three separate codebases. Having multiple implementations of the same thing always leads to inconsistencies. That’s frustrating for users, and it’s a waste of time and source of bugs for developers.

React Native helps with the frontend part of that question, since it lets you share knowledge, code, and perhaps even entire UI components across platforms. But most mobile apps today are still built with native technologies, and sharing UI code only solves half of the problem. The other half is data loading and management.

GraphQL: Perfect for cross-platform apps

The Apollo community wouldn’t exist without GraphQL. We owe a great deal of gratitude to its creators at Facebook, and people in the decades before who worked on technologies that inspired it. Here are three things that make GraphQL a game changer:

  1. GraphQL gives frontend teams the power to develop new products without waiting for backend changes.
  2. GraphQL enables great performance and caching on mobile with very little effort thanks to its simple query language and type system.
  3. The emerging ecosystem and tooling around GraphQL makes developers vastly more productive.

Looking at these advantages, GraphQL is a no-brainer for most teams starting new projects, and more and more teams are incrementally adding it to their existing infrastructure. Because every team is different and there is no one-size-fits-all solution, we work hard to make Apollo technologies easy to start with while being as flexible and modular as possible. Compatibility and flexibility have been in our design principles from day one.

Improving existing apps with GraphQL

One of the best parts of GraphQL is that it can be added incrementally on top of any API or backend data source. Companies that are already at the scale and complexity where you really start to feel the pain of REST or an ad-hoc API can add GraphQL a little bit at a time.

React Native can be the same way — most companies are currently writing one codebase each for web, iOS, and Android, but you can incrementally move your mobile apps over to React to start duplicating less and less logic. That’s exactly what Airbnb are doing with their mobile apps, but as they have started sharing more of their business logic and components, they’ve found that it still makes sense to implement some infrastructure in native code. Leland Richardson from Airbnb explained this concept in his talk earlier this year at React Conf, “React Native in the Brown Field”.

A diagram of native vs. JavaScript code in React Native from Leland’s talk.

Leland also announced one of Airbnb’s newest open source efforts, Native Navigation — a navigation package for React Native that allows both JS and native code to interact with navigation, enabling applications where views are mixed between React and native code to still have smooth transitions between these views.

Airbnb’s use-case in a nutshell

Airbnb has a desktop web application that uses React and mobile apps for iOS and Android. Parts of these apps are starting to migrate to React Native, which lets them develop much faster and share more code between platforms. That means that in their apps, React Native and native code live side by side and need to integrate seamlessly. This integration needs to extend to data loading and caching as well: If a React Native view loads data into the GraphQL cache, they need that data to be available in native views and vice versa.

Imagine, for example, a native view that displays apartment listings in a city, along with a picture, the price, and some preview text. When you click on a listing, it takes you to a detail view written in React Native. The app already loaded enough data to show you a nice preview (picture, price, title) while fetching the complete listing, but it can only do that if the React Native view has access to the data loaded in the native view.

Airbnb’s GraphQL client requirements (slide from Ben Schwab’s presentation on Apollo Android)

The best way to make this possible is to share the same GraphQL data store between all platforms. If, in addition to sharing the same store, those platforms also provide the same semantics for data fetching and cache policy, it becomes much easier to migrate views between platforms as necessary. You can use native UI code in one component, and React Native in another, wherever it makes sense.

With the help of developers from Airbnb, Shopify, The New York Times, and the whole Apollo community, we’re currently improving all three Apollo client designs and implementations to satisfy these requirements. The core of the architecture we’re building towards is a clean GraphQL store API that lets us share a pluggable cache between the three platforms.

Apollo’s uniform GraphQL store API

The heart of the uniform client architecture will be a shared global cache. It stores all normalized data and has a standardized API for reading and writing data. The store is composed of two levels: The higher level, which we currently call the Apollo Store, provides thread-safety and methods for manipulating the data using GraphQL primitives, while the lower level, the Normalized Cache, is a pluggable key-value store used for persisting individual records. This architecture will be identical across all platforms:

Apollo Client’s new store architecture

Having a clear contract between the store, the normalized cache, and the rest of the client implementation makes it easy to plug in any store: SQLite, IndexedDB, Realm, Mobx, Redux, NgRX, a plain in-memory dictionary, or anything else that suits your needs.

This design will make it easy to share the cache between native and React Native. In a typical mobile app with React Native, we expect the native client will handle the persistent store, and the JavaScript client will communicate with the native store over the React Native bridge to read and write data. We believe this will result in a lot of positive aspects even for apps built fully in React Native, including easy offline data management, smoother UI from offloading work away from JS, and prefetching data from native code before React Native has even finished initializing.

Sharing GraphQL data between JavaScript and native code

This new decoupled cache API is at the core of the new client architecture, but it is just one part of our uniform data loading plan. We are also working on making sure that fetch policies and cache invalidation/eviction strategies are consistent across platforms. We need this symmetry to make sure that views or data management logic can be moved across the platform boundary without leading to unpredictable errors. But on a more basic level, we’ve found that people contributing to the different client implementations learn a ton about the GraphQL client design space by sharing designs informed by the advantages and limitations of three very different platforms.

Get involved

I’m extremely excited about the future of Apollo and GraphQL. I think the new modular architecture and pluggable GraphQL store API we are working on will offer more flexibility without making Apollo Client any harder to use. You’ll still be able to get started with just a few lines of code, and then pull in packages from the ecosystem to expand Apollo Client with better interactivity, offline data, and more.

If you’re excited about Apollo and GraphQL as well, let us know in the comments, and join our active community on Slack! In addition to all of the Apollo contributors, there are tons of developers on there excited to talk about GraphQL and help new developers get started.

Have an idea for how to improve Apollo? Found a bug or have some spare time to give back to the community? Then join our #contributors channel on Slack, file an issue or make a PR directly on GitHub (JSiOSAndroid)!

Written by

Jonas Helfer

Jonas Helfer

Read more by Jonas Helfer