Navigating your transition to GraphQL
Last month, Sashko and I presented at the first ever GraphQL Summit, where we gave a talk about our philosophy for GraphQL-First development: a new workflow that we’ve put together after building from scratch with GraphQL ourselves and having hundreds of conversations with developers who are doing the same.
Among other benefits, we’ve found this workflow forces the frontend and backend teams to communicate more about product specifications and data schemas up front, allowing them to then build the app in parallel with a higher likelihood of success when connecting the two components together.
Our talk distilled the lessons we learned from building open-source tools for the GraphQL community and our experience going from zero-to-graphql in two production applications, including Optics, a GraphQL analytics tool I’ve been working on over the last few months.
In this post, I’ll dive into the details of the GraphQL-first workflow along with real world examples in four sections:
- Designing your schema
- Streamlining your workflow
- Loading data into your UI
- Monitoring your apps in production
Let’s get into it!
1. Designing your Schema
The first step of GraphQL First development is defining your schema. It becomes the contract between your frontend and backend developers, and is the foundation on which the rest of your development will be built.
If you’re building an app from scratch, the strongly-typed nature of the schema enables more efficient parallelized development. Frontend developers know exactly what data they can expect, while backend developers have a complete spec to develop to. Using GraphQL mocking tools and component-first development, you can build the frontend before having an actual backend, and connect the two painlessly when ready.
Alternatively, if you already have existing backends and REST APIs, you can take this opportunity to redesign your data layer and get the API you always wanted.
We had the experience of doing both of these things in our two production apps Galaxy and Optics.
In Galaxy, we have one page in particular that is extremely data intensive (see below). The page renders graphs that report statistics from Amazon DynamoDB in real time. For a typical app, the page can easily be rendering 30 live-updating graphs, each of which did its own data fetching from DynamoDB and MongoDB. Unsurprisingly, users were having performance issues with this page, and our app code to render the graphs was very complicated.
We were able to adopt GraphQL specifically for this page, and abstract away the complexity of our frontend code into the GraphQL layer. This enabled us to combine what used to be multiple client-side fetches into one, more efficient GraphQL query. In the end, we significantly improved the performance of the page and made our frontend code much more maintainable.
With Optics, we had the opportunity to build the app from scratch using GraphQL. We started our development process by designing the schema based on our wireframes and knowledge of the app’s data requirements. Once the schema was agreed upon, our frontend and backend teams developed separately for several weeks. When ready, we were able to easily connect the two systems in less than two days.
One question: Schema ownership
Interestingly, when you design apps schema-first, you encounter the question: who really owns the schema?
The schema design process is a negotiation between the frontend and backend teams, which both have different priorities. Our Optics schema design landed somewhere in the middle of the spectrum, but if you follow the GraphQL-First mantra, you’ll find yourself faced with a similar question. It’s up to you to make the right decision for your situation.
Now that you’ve designed your GraphQL schema, it’s time to build your app.
2. Streamlining Your Workflow
Traditional app development against a REST API can be hard to parallelize because it takes a lot of work to write and maintain mock data for every endpoint. This means that development on the frontend is often blocked by progress on the backend, and the two cannot happen at the same time.
Luckily, GraphQL makes the process much easier, because creating and maintaining mock data is simple with a static type system. With GraphQL, data is mocked per-resolver, rather than per-endpoint. Thus, if one of your schema fields changes during development, all you have to do is modify one resolver from your mock GraphQL server. If you were mocking data for a REST API and you migrated one of your database fields, you would then have to go and update every the logic for every endpoint mock you wrote, which is far more cumbersome and less maintainable. Check out this post for more details on mocking a server with GraphQL.
While we were building Optics, being able to develop the frontend and backend in parallel was crucial. Using the GraphQL First approach, we were able to painlessly adapt to schema changes, connect the frontend and backend in under 2 days, and develop the entire app from scratch in less than 3 months.
graphql-tools: Enabling GraphQL-First development
One of biggest focuses of the Apollo team is building tools that streamline the experience of developing apps with GraphQL. The first of these is the aptly-named graphql-tools.
This is an npm package that enables you to use the GraphQL schema language directly to implement a GraphQL server and mock data for that server. The code snippet on the right half of the slide above is all you need to implement a working GraphQL schema using graphql-tools.
Easily mocking data is another critical component of GraphQL-First development. Once you have agreed on the schema that represents the contract between your frontend and backend, you can implement both in parallel, and to do so you need a tool that enables you to mock out the backend data. graphql-toolstakes advantage of the GraphQL type system to let you mock data per-type or per-resolver.
The most exciting thing about building open source GraphQL software is collaborating with the community. These tools are made possible by great pull requests, like this one made by itajaja in August to enable mocking for union and interface types. There are also great people coming up with new and exciting ways to use the technology, such as this awesome talk by Nick Nance from Credit Karma about managing GraphQL schemas with graphql-tools.
Once you have designed your schema and can mock out the server data, it’s time to implement the frontend and backend in parallel. In this talk, we focus on the frontend.
3. Loading data with GraphQL queries
The schema is what makes GraphQL a great tool to work with when defining the data available in your backend. But the queries are what makes it so awesome for building UIs on top of that data.
It’s simple to send a GraphQL query to a server and get a JSON response: You can just send an HTTP request as documented on graphql.org. But, because of the special features of GraphQL, you will get some benefits out of using a fancier tool we’ll call a “Caching GraphQL client” — this keeps track of the data you’ve already loaded into your UI, and lets you read and manipulate that data in different ways.
So while it’s easy to load GraphQL data with a simple HTTP fetch, and that’s a great option for simple apps, in a more complex application you’ll want the power provided by a smarter client like Apollo or Relay.
When we first started working with GraphQL, we put a lot of time into learning about different requirements people have for a GraphQL client. We saw these in our own production apps, and also talked to hundreds of developers about their experience with GraphQL. Here are some of the conclusions we came up with:
Clients should support incremental adoption
Because GraphQL is at its best when adding it to an existing complex architecture, with multiple data sources and clients already present, incremental adoption is of paramount importance. This manifested itself in three ways:
- GraphQL on the client should be compatible with existing investments, such as Redux, React-Router, and other technologies. Even if you’re building from scratch, you never know when you’ll need to add something new to your stack, and it’s important that GraphQL works well with that.
- Best practices for GraphQL schemas are not yet well-established, and if you already have an existing backend you’ll need to fit your GraphQL schema over that data. For this reason, we concluded that GraphQL clients shouldn’t be opinionated about the backend schema.
- For incremental adoption on the frontend, it’s critical that GraphQL can be introduced into an application one page or component at a time.
The above points came up over and over again while we were thinking about adopting GraphQL in our own apps and talking to other developers.
Queries should be generated statically
Another question that came up was: Should queries be generated dynamically at runtime, or declared statically and baked into the app? After over a year of GraphQL, the answer is now clear to us: static queries are the way to go. For more details, check out:
- Our blog post, “5 Benefits of Static GraphQL Queries”
- Joe Savona’s talk, “Best Practices for GraphQL Clients”
Another big benefit of declarative queries is the ability to use GraphQL developer tools for static type generation, IDE integration, and query validation. Read more about code generation in Apollo-iOS, and check out apollo-codegen to contribute to Flow and TypeScript efforts!
For all of the above reasons and more, we have been working with the community for over 9 months to build, maintain, and improve one of today’s most popular GraphQL clients, Apollo Client. Version 0.5 was released recently, and is the culmination of more than 6 months of work from over 100 people, with special thanks to James Baxley III, Robin Ricard, John Pinkerton, Abhi Aiyer, and Kamil Kisiela.
We’re really proud of how far Apollo Client has come, and we’re not intending to slow down. We’re going to keep pushing for futuristic GraphQL features like subscriptions and next-generation developer tools to make building apps with GraphQL easier than ever.
Now that you’ve designed your schema and built your UI and backend in parallel, you need to run your app in production. What kinds of benefits can GraphQL bring there?
4. Monitoring your App in Production
If you’re going to be running an app in production, one of the first things you’ll need to set up are some performance monitoring tools to track things like server load, endpoint usage, and other classic questions. One of our favorite things about GraphQL is that it not only gives you more control over your API, but it also gives you more detailed insight into your API’s performance.
With GraphQL you can ask questions about performance that you’ve never been able to ask before. For example, since GraphQL APIs know exactly which fields their users are asking for, you can ask: which fields are unused — can I deprecate them? You couldn’t do this with REST, because you can’t tell what specific data from an endpoint clients are actually using. With some basic instrumentation of a GraphQL server however, this question is easy to answer.
Since GraphQL queries are executed one field at a time, you can easily determine performance hotspots by monitoring exactly how long each field in your a schema takes to resolve. Additionally, since you know who is using which fields and when, you can detect breaking schema changes, such as which frontends are making incorrect queries, or the whether a change to the backend will break some client out in the field.
There are lots of other questions like this that you can ask about a GraphQL server that we’re just beginning to explore.
As a proof of concept, we instrumented Galaxy with a prototype version of our monitoring tool and were able to immediately identify areas where we could improve its performance.
The following picture shows a detailed trace of resolver timings for a query that’s fetching metrics for a list of containers. A quick glance will show you that it’s asking for the info from each container serially. We saw this and immediately realized that the query execution process should be parallelized. We improved the performance of this query by 200 milliseconds by making that change.
Parallelizing processes like this one is key to maintaining the performance of a production app and ensuring that users have a good experience. Had we not done our own instrumentation of Galaxy, we might not have identified this area for improvement.
Update: There is a new version of Optics! Apollo Engine has everything Optics does plus error tracking, query caching, and more. Engine is free for 1 million requests a month!
To sum everything up, we’ve been very excited about the developer experience enabled by GraphQL. We think that the best way to do GraphQL development is to design your schema first, which then acts as the contract between your frontend and backend teams. We’ve been working tirelessly to create both open source tools for GraphQL development and monitoring tools for production apps. It was our pleasure to host this conference, and we’re really looking forward to seeing where 2017 takes the GraphQL community.
If you’d like to work with us on building more awesome GraphQL tools, we’re hiring!