GraphQL: Building a consistent approach for the API consumer
Developers use application programming interfaces, or APIs, to assemble data and functionality for new mobile or web apps, but when it comes to interacting with APIs, developers are often faced with two popular options: REST or GraphQL.
In this article, we’ll explore how these approaches compare, and we’ll offer REST API best practices that can be applied to build a more consistent experience for GraphQL API consumers. One option is not better than the other, and both can be used within the same teams if not the same projects--but regardless of what kind of APIs a project entails, a more consistent experience will help developers do more, faster.
REST and GraphQL compared
REST is a software architectural style to which APIs conform so developers can interact with services in a standard way. GraphQL is a query language for APIs and a runtime for fulfilling those queries. REST and GraphQL are similar in that they identify resources as URLs through which the app can fetch data or functionality—but there are many differences:
GraphQL exchanges data at a single endpoint whereas REST often involves several endpoints. GraphQL resolvers retrieve the data for fields, and if one resolver fails, the rest of the query can still retrieve and return useful data. This interaction paradigm mirrors what’s expected from doing multiple REST queries, and as such, one GraphQL query frequently replaces multiple REST queries.
GraphQL prevents over-fetching and under-fetching of data—that is, an endpoint responding to a call with too much or too little information, respectively, compared to what the app needs. REST APIs are offered in various levels of resolution. Some retrieve more data, and some retrieve less data. This means an app might receive too much data, such as the whole employee profile when all that was needed was the employee name and ID number. Likewise, it might receive too little data, forcing the app to make several API calls instead of just one.
REST uses HTTP verbs, and generally uses JSON in order to exchange payload data, but in GraphQL, the HTTP POST verb is most frequently used, and the different query types are specified inside the protocol. GraphQL also uses a custom query format called Schema Definition Language (SDL), and even though that custom query language is used for the request, JSON is returned, which makes it easier for clients to leverage the response. GraphQL client libraries feature native integration with the ReactJS UI framework, and are also available for other other languages and paradigms, making them accessible to many developers today.
The developer’s discovery perspective differs. To understand how REST APIs work, the developer typically uses a portal as the storefront to discover and interact with the APIs. In GraphQL, the portal is a built-in playground that also accommodates development. It's almost like an integrated development environment, allowing developers to explore new queries on the fly, assisted by features like tab completion. Documentation is also different. REST usually uses OpenAPI specs and portals. Some extensions to OpenAPI exist. For example, Apigee SmartDocs builds interactive documentation from those OpenAPI specifications. GraphQL developers typically use schema-based interactive documentation, such as Graphiql to develop and interact with GraphQL endpoints.
These qualities make GraphQL popular for an increasing number of use cases but can point to possible adoption challenges. For projects involving interoperability and decomposition of internal infrastructure, GraphQL is a useful tool for creating a few APIs for many disparate legacy systems. But it can also be leveraged in self-service developer programs and related growth strategies, which typically involve enterprises encouraging internal and external innovation by making REST APIs available via an API management platform.
These programs differ from traditional infrastructure-centric API projects in that the APIs may be used by many people outside the team that built it, for many uses that team never imagined. This reiterates the importance of a consistent, reliable, intuitive developer experience--and it also raises one of the obstacles to adapting GraphQL: it is relatively easy to glance at a group of REST APIs and intuit what they do and how they work, but we’re not yet as close to that with GraphQL.
Using REST-based practices in GraphQL
You should be open to using the best tools for the job, which may include both GraphQL and REST. To work more productively with GraphQL, we recommend adopting some of the REST-based best practices we’ve developed over years of experience building developer programs.
Think of APIs as digital products that let enterprises take their assets and, in order to increase the leverage of those assets, put them in the hands of developers, whether those developers are internal employees, partners, or external customers. Because APIs are digital products, developers need a consistent experience in order to understand how to use them, and to bring compelling experiences to market. Developer friction is a huge challenge in adoption of APIs and in growth strategies of digital companies, so just as with REST, consistency is key for GraphQL.
Treat the graph as a data-driven hierarchy defined by plural nouns
One of the key tenets of the REST architectural style is to create a simplified, consistent interface that rationalizes infrastructure complexity. One would never expect a well-formed REST query to be GET/listEmployeesByDepartment––that looks more like a Java function. Rather, a well-formed REST resource would use plural nouns: GET /Employees, then POST /Employees, etc. By reliably conforming to predictable expectations, REST APIs directly affect the speed at which developers are able to consume resources and build new experiences––and time is money.
GraphQL’s schema uses a graph hierarchy to define relationships between entities, such as the titles and authors of books in a catalog. This is a fundamentally data-driven hierarchy but we sometimes see it treated as a functional hierarchy that looks like a Java function––and this can introduce friction by disrupting predictable, intuitive, consistent experiences.
A well-formed GraphQL should look like a well-formed REST. If you can GET from /Books, it should be assumed you can POST to /Books. Compare that to a more Java-like construction, defined by a verb-based function instead of a data-based noun, such as GET listBooksByGenre. How can you POST? To /BooksByGenre? To /Books? To /listBooks? Who knows. Our advice is to be data-driven, and to treat the graph as a data-driven hierarchy.
Don’t force GraphQL when REST makes more sense
In REST, users often request and submit data from different URLs, especially when using patterns such as Command Query Responsibility Segregation (CQRS), a design pattern first identified by Martin Fowler that separates the model that reads the data from the model that updates the data. Developers often use CQRS in REST to retrieve data from multiple services in microservices architectures.
In GraphQL, mutations (the way a GraphQL developer submits data) can get very complex very quickly, especially when there are a lot of different data types or when very little data is submitted. We recommend using a style similar to CQRS that separates retrieving data from submitting data. This may be particularly useful to large enterprises, especially those that already have a REST-based API layer. GraphQL can retrieve data on top of or instead of the API management layer, but data can still be submitted through the existing REST APIs. This demonstrates a developer should not want to force GraphQL when REST makes sense.
Optimize for re-usability
Large enterprise GraphQL deployments often encounter challenges when many different types of backends need to be made available to developers. Different business units develop different aspects of the schema, which is then presented to developers as one comprehensive graph, frequently through schema stitching of schema federation. The challenges arise when the behavior of the queries returns different data or behaviors from different parts of the graph, because there’s no consistency in the representation of the data. Variable names that look the same in the SDL should not return different values or formats just because they resolve to different data sources.
Further, Relay cursor connections and input hints should all present uniform behavior regardless of the portion of the graph being requested.
This is particularly a problem with mutations, because if a developer submits data to one part of the schema one way, and it gets recorded one way, they may not realize that when they submitted it in another part of the schema, it was recorded another way. Since mutations are a particular challenge when optimizing for re-usability and API productization, we recommend being particularly conscientious of the way you develop and design them in GraphQL.
Lastly, let’s look at field names. It is developer-hostile to have field names with the same name provide different data and behavior because they are in different parts of the schema. For example, when there is a name field in one part of the schema that expects first, middle, last name, and a name field in another part of the schema that expects last name.first name, this incongruence can lead to developer abandonment.
In GraphQL, it's easy to optimize for query efficiency, but be intentional about optimizing for re-usability. Avoiding situations in which APIs confuse developers will pay dividends.
Whether REST or GraphQL, API is a product that needs to be managed
Most of these best practices, from treating the graph as a data-driven hierarchy to optimizing for re-usability and developer consumption, reinforce a central idea: APIs that are useful for growth strategies are products for developers, so the developer’s experience using the API is among the most important determinants of whether the API is adopted or not. With the help of Apigee API Management, developer programs have been embracing this idea for years with REST APIs, and as enterprises apply it more broadly to GraphQL, those API programs will only become more adept at empowering developers to innovate.
To learn more about GraphQL vs. REST, check out our video from Google Cloud Next. You can also view this community post for useful links to a reference implementation, along with links to a GitHub repo that provides tooling to enable GraphQL query authorization in Apigee.