As we keep growing at Thumbtack, we continue to invest in our infrastructure and developer experience. In order to move faster and in a more consistent manner across our customer and pro clients, our engineering team has decided to build a GraphQL based API server.
In this blog post, I will talk about the benefits of using GraphQL in general and on Android, and how it ties with our Android development workflow at Thumbtack.
GraphQL gives clients the power to explicitly request the data that they support and require. This helps to solve the over-fetching and under-fetching problems that usually arise with native clients using RESTful APIs. GraphQL also provides built-in support for a well-defined and strongly-typed schema that does not require versioning. Especially on Android, this helps to detect breaking changes at compile-time, and auto-generate query and response model classes. Moreover, GraphQL carries powerful mocking capabilities and tools such as Apollo and GraphiQL that make it easy to start client development before the server implementation is completed.
One of the main advantages of GraphQL is that it allows clients to explicitly define the response data that they need. This helps to eliminate the over-fetching problem, where the server sends extra data to clients that they don’t require. For example, our website shows more information on some screens than its native client counterparts. In this case, the native client might end up receiving unneeded data. The ability to explicitly specify the response data helps to reduce the used bandwidth and battery, especially on resource-constrained mobile devices.
GraphQL can also help with under-fetching and N+1-request problems, where a certain endpoint does not provide enough data for building a certain UI. In a scenario where clients need to make extra requests to build different parts of the UI, it is up to clients to manage and maintain multiple network requests. This can cause overhead while transmitting and synchronizing the extra data. However with GraphQL, the server can make these requests individually (i.e., to microservices, databases, etc.), combine the results and send back the exact response that is requested to efficiently build the UI.
The ability to explicitly define the response data also helps with maintaining backward-compatible changes without versioning. With this ability, clients receive back only the response data that they support. As long as the schema is updated with new fields and existing mandatory fields are not deleted but deprecated, older clients can continue using their existing queries and newer clients can start using the newer queries.
Well-defined and strongly-typed schema
GraphQL provides built-in support for a well-defined and strongly-typed schema. Any schema changes are reflected to clients once the updated schema gets published. Especially on Android, this allows developers to catch breaking changes at compile-time. In case of a breaking change in the schema, clients can be updated before the code gets shipped. While GraphQL warns at compile-time, with RESTful APIs without a schema, client and server engineers need to manually coordinate handling breaking changes. This might result in bugs that go undetected before the code gets shipped. For example, in a scenario when a field that the clients expect to be non-nullable gets changed and becomes nullable in the API response, the clients might behave incorrectly and even crash.
Thanks to the schema, GraphQL Android clients benefit from model classes that are auto-generated based on the schema and queries that are written for it. This removes the need for client engineers to manually write request and response model classes.
GraphQL comes with excellent mocking capabilities. Once the schema gets defined for a feature, clients can mock and customize the responses coming from a mock server. This allows client engineers to start building their UI and functionality without an actual server implementation.
Android development workflow with GraphQL
In order to call our GraphQL endpoint, we’ve adopted the Apollo GraphQL client for Android. After Apollo was set up, our Android development workflow boiled down to the following steps.
Designing the schema
We design the schema collaboratively among the client and server engineers. Since GraphQL does not support versioning, we aim to design the schema flexible enough so that it can be changed to support future needs. For example we make sure any data that can be optional or subject to change are not defined as mandatory fields. Once we decide on the final schema for a particular feature, we merge it to our backend repository.
Updating the schema in Android repository
Once the schema gets merged in the backend, we fetch it to our Android repository. Note that in case there are breaking changes in the schema, it is best to address them as soon as possible. Since GraphQL validates written queries against the schema at compile-time, not fixing those changes might block other engineers.
Write your query
Once we have the latest schema in our Android repository, we start writing queries against it. GraphiQL is a nice tool that helps us visualize the schema and write queries.
The “Document Explorer” on the right section of the GraphiQL UI lets us visualize our schema. It allows us to explore all the available queries, mutations and data types and warns us about any deprecated fields.
GraphiQL’s autocomplete and live syntax-checking features help us write queries. Once we write our query or mutation, we can execute it and see the mock response in the middle section.
Additionally, we are able to customize the mock responses for our queries and get back contextual responses. This helps us start building our features without an actual server implementation.
Once we have our query in Android Studio and have built our project, Apollo auto-generates the necessary Java classes for our queries and their responses. Note that currently, Apollo is not able to generate Kotlin classes. Therefore for certain UI-related response data that we wish to use with @Parcelize, we do a manual mapping from Java to Kotlin.
Using the auto-generated classes, the network call becomes no different than making a simple function call with well-defined input and output parameters. Traditionally we use Retrofit with RxJava 2 adapter to make network requests. Apollo also provides a RxJava 2 adapter which helped us add GraphQL functionality without changing our apps’ existing architecture.
GraphQL has many benefits over traditional RESTful APIs that can help you move faster and achieve consistency across different clients. The ability to explicitly define response data and having a well-defined schema allows a better development experience. This leads to building more robust and less error-prone apps. We plan to invest more in GraphQL as we move forward with building new Thumbtack experiences. Come and join us build stellar Android apps for our customers and pros!