Chapter 1. Introducing GraphQL
Of the thousands of new commercial and open source projects that are released every year, only a handful end up becoming widely used industry standards. GraphQL is one of those technologies for a very simple reason: it brilliantly solves a host of problems for a wide range of constituencies.
This book provides a solid overview of GraphQL and its application to commerce. In this first chapter, we cover GraphQL itself—the problems it solves, how it intersects with REST, and why it’s such a natural fit for commerce. In the second chapter, we look at the GraphQL specification itself, including key terminology, how to model your schema, and how to write queries, mutations, and subscriptions. In the third chapter, we explore GraphQL from a frontend developers’ standpoint and look at the role that clients can play. Finally, in the fourth chapter, we focus on GraphQL servers, which are actually responsible for executing queries against the schema and performing the requested action.
Note
We use the term “queries” to capture all of the operations that you can execute against a GraphQL server, even though GraphQL also supports mutations (changing data) and subscriptions (watching for changes to data).
Commerce Requires More Than REST
In the 1990s, commerce platforms shipped with the frontend and backend as one indivisible unit. In that decade, the web was the only channel and developers needed only to build a website. Given the immaturity of frontend frameworks, the commerce platforms had to offer the frontend as part of the commerce platform. “Headless” didn’t exist as a concept. REST hadn’t been invented yet and JavaScript first appeared in 1995. These early commerce platforms contributed to or invented much of the software used to dynamically generate static web pages, including JSP, JHTML, PHP, and ASP. The major problem with this approach is that any changes, no matter how small they were, had to be handled by IT and slotted in as part of a formal release. This upset marketers who wanted to be able to make changes on their own.
In the 2000s, content management systems (CMSs) like Day, Interwoven, and Vignette began to offer integrations with commerce platforms, whereby the commerce platforms would serve the backend and the CMS would serve the web frontend. This allowed IT to manage the backend and marketing to manage the frontend. Although this made life easier for marketers, the integration between the frontend and backend was hardcoded with what could only be described as spaghetti code. The two were permanently wedded and inseparable. This was all premobile, so, again, the only channel anyone cared about was the web.
In the 2010s, there was an explosion of consumer electronic devices that put the internet in everyone’s pockets. Mobile phones with fully featured browsers and native apps became widely used (see Figure 1-1). The Internet of Things (IoT) became real as the price of semiconductors and internet connectivity fell. Even devices as mundane as TVs started to become connected to the internet and could be used to facilitate commerce.
Commerce has transformed into something that’s part of our everyday lives, embedded into the dozens of screens and other internet-connected devices that we interact with on a daily basis.
While commerce was transforming, representational state transfer (REST) APIs were emerging as the default means of exchanging data between disparate systems. When compared to CORBA, SOAP, and the other standards that preceded it, REST was an enormous improvement. Developers could build a REST API, write code to implement the API, and then offer up the API to any system that wanted to call it and had proper access rights.
REST APIs are now the default means of exposing commerce-related data and functionality from applications/microservices. Any application or frontend can now consume that data and functionality to build an experience for a customer, whether on a desktop-based website or an IoT device on a 3G network in an emerging country.
Even though REST APIs are great for backend developers, they pose many challenges for frontend developers. As the number of clients capable of facilitating commerce continues to grow by the week, the power is progressively shifting from backend to frontend developers.
The Challenges of REST APIs
REST APIs are great, but like all transformation technologies the benefits don’t come without new challenges. Let’s explore.
Lack of a specification
REST doesn’t actually have an actual specification, because it’s more of an architectural style than a formal standard. The term REST was coined and its principles were put forth in Roy Fielding’s 2000 PhD dissertation titled Architectural Styles and the Design of Network-Based Software Architectures. Although his dissertation goes a long way toward outlining a vision, it’s by no means an actual detailed specification with rules that can be programmatically validated. In his dissertation, Roy explicitly says:
REST ignores the details of component implementation and protocol syntax in order to focus on the roles of components, the constraints upon their interaction with other components, and their interpretation of significant data elements.
On the other hand, HTTP, OAuth, JSON, and most of the other technologies that developers use on a daily basis are formal documented standards, each with thousands of pages outlining granular rules for what is and what is not considered valid. Take a look at how the JSON specification dictates how arrays should be represented:
5. Arrays
An array structure is represented as square brackets surrounding zero or more values (or elements). Elements are separated by commas.
array = begin-array [ value *( value-separator value ) ] end-array
There is no requirement that the values in an array be of the same type.
There’s no ambiguity about this and any JSON-compliant document will represent arrays the exact same way, every time. Standards promote interoperability.
The term “REST” could and often is applied to any human-readable API offered over HTTP that returns JSON or XML as a response type. There could be substantial differences in error handling, authentication, authorization, response types/formats, query parameter format, and so on. Because of these inconsistencies, client-side developers need to work with the idiosyncrasies of each API that they want to call, which in the case of a large data-intensive web page could be dozens.
There are a few standards out like Open API and OData but these standards have differences in what parts of REST they choose to include in their respective specifications. For example, Open API allows for one of four authentication schemes, whereas OData allows for any authentication scheme. Even within the specifications, there are inconsistencies that make lives difficult for frontend developers. For example, the Open API specification allows responses in both JSON and XML formats. Even with a catalog of Open API–compliant APIs, frontend developers might need to embed a lot of frontend logic to account for implementation differences.
Not knowing which API to call
The same data (product, price, inventory, order, etc.) or functionality (decrement inventory, calculate price, mark an order as having been shipped, etc.) might be available in many different systems across a typical enterprise. It’s common that an enterprise resource planning (ERP), order management system (OMS), warehouse management system (WMS), commerce platform, and myriad custom applications all touch much of the same data and offer similar if not identical data and functionality, all while claiming to be the “single source of truth.” Each of these systems might offer different levels of data freshness, different ways of accessing the data (REST APIs, SOAP APIs, batch feeds, etc.), different performance and uptime guarantees, and so on. Some of the APIs might not even be publicly available.
A common example of this issue is inventory—who actually owns it? Does the commerce platform? The WMS? The OMS? When you query the WMS or the OMS, do those systems take into account inventory that’s in users’ shopping carts but not yet firmly reserved through a placed order? There are so many nuances that it can be challenging for a frontend developer to discover which APIs to call and what data or functionality each offers and then figure out how to call each API.
To make matters worse, REST doesn’t have introspection. The clients and servers aren’t working against a fixed contract: clients can send anything and servers can respond with anything.
A common fix for this is an internal wiki page documenting all of the available APIs that clients can call, but those are often stale and their effectiveness is limited by organizational silos.
Overfetching data
A common problem with REST APIs is overfetching data. A typical SKU could have hundreds of attributes, ranging from the standard attributes like display name and long description, all the way to manufacturer-specific codes and logistics-related information. Although the commerce platform has all of these details, each client does not. An Apple Watch client needs only the display name, images, and maybe a short description. Retrieving the entire product is the easiest for a frontend developer:
HTTP GET for /products/12345
This works great within the confines of a datacenter, but imagine having an Apple Watch fetch an entire two-megabyte payload over a cellular network every time a user wants to look at a product’s details? Multiply that across every API needed for an app to function and you could have overfetching that leads to serious performance degradation.
There are two general approaches for solving this problem. The first is to specify in the HTTP request exactly which fields are required:
HTTP GET for /products/12345?attributes=displayName,images,shortDescription
But not all APIs support this, and the format of how those fields are specified tends to differ based on which API you’re calling. Again, remember that REST doesn’t have any formal standards and the ability to retrieve only certain attributes is not part of that specification. Assuming it’s even possible, frontend developers would need to build and maintain these long unruly URLs.
Another approach is to build “backends for frontends,” which are little applications that return exactly what’s required for a given client. There might be an “AppleWatchProductDetail” microservice that exposes an API that gives the Apple Watch application exactly the few fields it needs. That microservice then calls the full product API.
The challenge with this approach is that you need to maintain hundreds of microservices, and each of those microservices is tightly coupled to the client and to the backend APIs. Every new client now requires a few dozen new microservices, as illustrated in Figure 1-2.
This certainly works, but it comes at the cost of higher maintenance. Many client-related changes and many backend API changes require updating each of the many backends for frontends on which those clients and APIs rely.
Underfetching data
Related to overfetching data, another problem with REST APIs is underfetching data. To render an order history web page, for example, you’ll first need to retrieve orders placed by a given customer. Next, you’ll need to retrieve the shipment status of each order. Then, you’ll need to find out what a customer can do with each order, such as whether an order can be returned. Often times, the requests to the different APIs must be made serially because the data from one API is needed to make the call to the next API, as demonstrated in Figure 1-3.
Making multiple, serial round-trip calls from clients (which might have limited computing power, limited bandwidth, and high latency) is very expensive. Devices, especially the lower-power IoT-style devices, can have limited processing capacity, and making so many HTTP requests can consume too much power. Devices like smart watches and mobile phones are often connected through cellular networks, which have low bandwidth and high latency, both of which are very bad for performance.
Also, all of those sequential calls amplify any performance issues your APIs might have. Making 10 parallel calls to APIs that respond in an average of 200 milliseconds results in 200 milliseconds of perceived latency to the end customer. But making the same calls sequentially could result in more than two full seconds of perceived latency. When you add in rendering and other overhead, you could easily be rendering pages or screens in seconds rather than milliseconds.
Finally, making all of those calls from the client requires the client to be fairly intelligent. It needs to know which APIs to call and in what order. When multiplied across the dozens of different clients, this could lead to a proliferation of logic in clients that is undesirable for frontend and backend developers alike.
Underfetching can sometimes be solved by having multiple copies of data in each microservice. Going back to the order history example, you could have an order history microservice that has copies of every order, the shipping status and available actions of each order, and the full product details of the products. The major downside to this is the intelligence that each microservice now needs to have. Rather than specializing on one function, each microservice needs to perform tangential functions. Besides the sheer volume of duplicate data (a single order could have multiple megabytes worth of data), this expands the amount of work that each team needs to perform. It can also be difficult for clients to know where to retrieve data when there’s so much duplication. The final problem with this approach is that the data each microservice has isn’t always the most up to date. For example, the shipping status microservice has the most up-to-date data but if that data is being copied over to the order history microservice, the order history microservice will by definition have out-of-date data.
The other way to solve this is to build a backend for your frontend, like how you’d solve the overfetching problem.
What Is GraphQL?
Although REST is great, its lack of standardization, lack of discoverability, and overfetching and underfetching all cause headaches for client-side developers. GraphQL can solve these problems and many more, all while building on top of—not replacing—REST.
What Are Graphs?
Before we formally introduce GraphQL, we first must explain the concept of a graph. After all, “Graph” is the defining part of the name “GraphQL.”
Graphs are a collection of interconnected objects. Here are some common examples in the real world:
-
Transportation networks like air, rail, and auto
-
Family trees
-
Social networks like Facebook, Twitter, and LinkedIn
Graphs also underpin much of IT as well, including the following:
-
The World Wide Web (Google built its company on graph theory, which underpins its famous PageRank algorithm)
-
XML documents (including HTML documents, which are a subset of XML)
-
Networks (routers themselves are nodes)
Any time you have a collection of interconnected objects, you have a graph. When you look around, you’ll find them everywhere. Graph theory, a subset of discrete math, is the formal study of the relationships between graphs.
When you look at a social network like Facebook, it’s a big graph. “Friends” are bidirectional relationships between two nodes in a graph. Twitter is another example of a graph, but the relationships can be one-way (I follow Martin Fowler but he doesn’t follow me). In graph theory, this is known as an undirected graph. Given that Facebook and Twitter are essentially big graphs, it shouldn’t be a surprise that Facebook invented GraphQL and Twitter was an early prominent user. Both of their businesses (along with Google) owe their success to having properly understood graph theory.
Commerce Graphs
Commerce is full of graphs. There are objects (customers, orders, products, etc.) and relationships between those objects (customers place orders, which contain products). Figure 1-4 shows a graph with five directed relationships.
A foreign key in a database is a relationship between two nodes in a graph.
REST is definitively not graph-based. It’s great for retrieving single objects (customers, orders, products, etc.) and maybe their dependencies (the products contained in an order, etc.), but that’s it. REST would be the equivalent of being able to select data from only one database table at a time, whereas GraphQL allows you to select data from multiple tables in one query.
Now that we’ve covered what graphs are, it’s time to actually explain what GraphQL is.
In the Beginning…
In the 2010s, Facebook began to have the same problems with REST as outlined earlier. The company had more than a billion active users in 2012. Those billion-plus users accessed Facebook from every conceivable device in every conceivable configuration. At the time, Facebook supported desktop web, mobile web, and iOS/Android wrappers (which were thin wrappers over mobile web). The iOS and Android applications were widely panned, but Facebook was limited in its ability to create native clients due to the limitations of REST.
In 2012, Facebook set out to build entirely new native mobile apps. Lee Byron, Nick Schrock, and Dan Schafer came together, and by early March they released an initial prototype of GraphQL that was then called SuperGraph. Later that year, Facebook released GraphQL internally in more or less its modern form, with it powering the company’s native apps. The desktop web and mobile web clients also switched over to GraphQL after its success was demonstrated with the native apps. Today, every time you pull up Facebook from any client, you’re using GraphQL behind the scenes.
Because of GraphQL’s success internally, Facebook released its specification externally, including a JavaScript-based reference implementation in July 2015. In September 2016, GraphQL left the “technical preview” stage, meaning that it was mature enough to be considered production ready.
Facebook originally patented GraphQL in 2012 and then licensed it as BSD+Patents when the company publicly released it. Although this license did help to protect Facebook and the GraphQL community from patent trolls, it prohibited users of GraphQL from ever having the legal right to sue Facebook for any patent infringement, even if it wasn’t at all related to GraphQL. Bowing to pressure from the GraphQL community, Facebook relicensed the specification under the much more permissive Open Web Foundation Agreement (OWFa), which grants GraphQL users full patent rights and makes it easier for other organizations to contribute. GraphQL.js, the reference implementation, was also relicensed under the extremely permissive MIT license.
In November of 2018, Facebook created the GraphQL Foundation and donated the GraphQL intellectual property to it. The GraphQL Foundation is a top-level member of the Linux Foundation, and a peer to the Cloud Native Computing Foundation (CNCF). The GraphQL Foundation’s managing board is composed of senior leaders from Apollo, AWS, IBM, PayPal, Twitter, and others, and their responsibilities are as follows:
-
Provide a roadmap for the GraphQL specification
-
Oversee changes to the specification
-
Provide legal, marketing, and community facilitation support
Today, GraphQL is used by hundreds of enterprise-level organizations. The major cloud vendors along with other software vendors of all stripes (including the commerce platform vendors!) have, or are adding, GraphQL support, as well.
Introducing GraphQL
GraphQL is a formal specification for retrieving and changing data, similar to how SQL is used to change and retrieve data from database tables. At a very high level, GraphQL describes the language and grammar that should be used to define queries, the type system, and the execution engine of that type system.
A very basic example of a GraphQL query is something like the following:
query { product(id: "94695736", locale: "en_US") { brandIconURL name description price ableToSell averageReview numberOfReviews maxAllowableQty images { url altText } } }
The GraphQL server would then respond with something like this:
{
"data"
{
"product"
:
{
"brandIconURL"
:
"https://www.legocdn.com/images/disney_icon.png"
,
"name"
:
"The Disney Castle"
,
"description"
:
"Welcome to the magical Disney Castle!...."
,
"price"
:
"349.99"
,
"ableToSell"
:
true
,
"averageReview"
:
4.5
,
"numberOfReviews"
:
208
,
"maxAllowableQty"
:
5
,
"images"
:
[
{
"url"
:
"https://www.legocdn.com/images/products/94695736/1.png"
,
"altText"
:
"Fully assembled castle"
},
{
"url"
:
"https://www.legocdn.com/images/products/94695736/2.png"
,
"altText"
:
"Castle in the box"
}
]
}
}
}
That response then could be used to render a product detail page as shown in Figure 1-5:
Rather than interacting with the multiple APIs that hold this data, the client-side developers can make one GraphQL query against an endpoint that’s typically exposed as /graphql. The GraphQL server then validates the query and calls what are known as “resolvers” to retrieve (or modify) data from the underlying APIs, protocol buffer (protobuf), datastore, or any other source system. GraphQL doesn’t take a stance on what programming language you should use, how to retrieve data from resolvers, or much of anything else. GraphQL is a specification, not a specific implementation.
Note
Even though GraphQL is a specification, GraphQL’s official reference implementation underlies most of the GraphQL servers on the market, including Apollo and Relay. It was first released by Facebook in 2015 and has emerged as the community standard.
To this point, we’ve talked only about GraphQL queries (retrieving data). Besides queries, GraphQL supports three other action types:
- Mutations
-
Creating, updating, or deleting data, then an optional read after data is written
- Subscriptions
-
Watching for updates to data
- Introspections
-
Retrieving metadata about what types of queries can be performed
With support for all three operations, client-side developers can build any experience imaginable.
Benefits of GraphQL
GraphQL offers many benefits. Let’s explore some of them.
Easier frontend development
GraphQL’s primary benefit is that it empowers frontend developers. Given the wide array of devices and clients you need to support, it’s possible to have many more frontend developers than backend developers. Those developers must also iterate much more quickly than the backend. Browsers, operating systems, frontend frameworks, end-customer tastes, and so on all change so frequently that each of these teams need to be releasing constantly. GraphQL is the layer that decouples the frontend teams from the backend teams and allows frontend teams to rapidly innovate.
Frontend developers no longer need to hunt for APIs or try to understand which APIs have the most up-to-date version of data. That complexity is handled by the GraphQL layer. If there are four different APIs holding inventory data, it’s the GraphQL layer’s responsibility to identify which API should be made available to the clients. Now, each team doesn’t need to go through the exercise of discovering each API and hoping they’re using the right one.
GraphQL also handles the intricacies of calling each source system (API, protobuf, datastore, legacy system, etc.). Source systems can use different transport protocols, different data formats, and different authentication and authorization schemes, and can have different idiosyncrasies in how they’re called. Frontend developers don’t want to deal with that. Duplicating all of that logic in every client is difficult, expensive, and slows down overall development velocity. With GraphQL, every time a backend system changes (i.e., from XML to JSON data representation), it’s completely transparent to each client. The change can be solely implemented in the GraphQL layer. Clients should be as free of frontend logic as possible.
Another advantage of GraphQL is introspection. Frontend developers can access one endpoint (typically /graphql), and using an integrated development environment (IDE) or even by calling GraphQL directly (see Figure 1-6), they can quickly understand what types (objects like customers, orders, and products) and fields (attributes like firstName, shippingAddress, and displayName) are available.
Because GraphQL is introspective, every single query is validated before it’s executed. Again, it’s similar to SQL in that regard. Developers immediately know whether their queries are valid, even if the source systems aren’t available.
Improved performance
Next, GraphQL dramatically improves the performance for end customers.
GraphQL makes data from multiple source systems available in one JSON document. Frontend developers specify exactly the data that they need, and GraphQL provides it by making parallel requests to the source systems that have that data. Within a datacenter, latency, bandwidth, and computing power are basically unlimited. It’s far more advantageous from a performance standpoint to make all of those requests within a datacenter and then offer up the client a single document containing that data. Clients are often connected over mobile networks, which are bandwidth and latency constrained. Client devices are often constrained by their computing power. Making all those HTTP requests has a cost, and small IoT-style clients, wearables, and other devices don’t have that much computing power to work with.
Also, GraphQL completely eliminates the problems of over- and underfetching. Frontend developers specify exactly what they need and GraphQL provides it. There is likely to be some over- and underfetching when the GraphQL layer calls the source systems, but again, within a datacenter resources like latency and bandwidth are essentially unlimited.
Less code to maintain
Finally, GraphQL leads to there being far less code to maintain. As previously discussed, clients don’t need to replicate the logic required to call different source systems. Clients can execute queries against a GraphQL server rather than having to write complex authentication code for each source system. Any changes with how source systems are called can be made in one location.
Also, GraphQL completely eliminates the need for backends for frontends. Clients can query the GraphQL layer for everything they need. Not having that intermediary layer dramatically cuts down on how much code needs to be written, tested, maintained, and run.
Drawbacks of GraphQL
Like all new technology, GraphQL isn’t a silver bullet that magically fixes all of your problems.
GraphQL is another layer that must be maintained with its own architecture, development, operations, and maintenance needs, though it does eliminate the need for dozens or hundreds of backends for frontends.
Also, security, as we discuss in Chapter 4, can be challenging with GraphQL. The GraphQL specification leaves out security entirely, leaving it up to each vendor.
The final challenge with GraphQL is that it can be difficult to combine multiple GraphQL endpoints and schemas. Frontend developers want one endpoint (/graphql) with one schema, but different teams and different vendors will all have their own endpoints and schemas. The commerce platform vendor can expose its own /graphql endpoint and schema, for example. Your cloud vendor, CMS vendor, search vendor, various internal teams, and so on might all expose their own endpoints and schemas. Frontend developers then need to access multiple endpoints. At that point, why even bother with GraphQL when REST is already available? Fortunately GraphQL server vendors do offer a way to offer your frontend developers a single GraphQL endpoint and schema, which we discuss in detail in Chapter 4.
GraphQL Compared to REST APIs
Part of the reason REST has become so popular is because microservices have emerged as the default architecture for building many cloud-based applications and commerce applications in particular. As Figure 1-7 shows, microservices are individual pieces of business functionality that are independently developed, deployed, and managed by a small team of people from different disciplines. For more on microservices, take a look at Microservices for Modern Commerce (O’Reilly, 2017).
Microservices by definition require an API, given that a microservice’s data can be accessed only through an API. In other words, a client or another application cannot directly read or write to a microservice’s datastore. REST APIs are what most choose to expose.
With REST, developers are optimizing for east/west communication (communication within a datacenter between applications), not north/south communication (communication from a datacenter to a client, as demonstrated in Figure 1-8). Developers don’t care much about the latency/bandwidth/computing power constraints of their clients. Their scope is within a datacenter, where all of those resources are basically unlimited.
Too much focus on east/west communication leads to developers automatically returning all data from an API by default. If a product has 200 attributes, any given developer is likely to write the application to return all 200 of those attributes. A product could be hundreds of kilobytes or even megabytes. The client has very little say in what data is returned. Developers don’t have much visibility or concern for what happens after the data is exposed in an API. With GraphQL, developers specify exactly what they need. GraphQL shifts the power from the backend developers to the frontend developers.
As previously discussed, REST is more of an architectural style than a formal standard. GraphQL, on the other hand, has a very strict specification, which we cover in greater detail in Chapter 2. GraphQL’s strict standard supports introspection, which allows frontend developers to quickly see which types and fields are available. REST can allow developers to discover types through Hypermedia as the Engine of Application State (HATEOAS).
<product
id=
"{id}"
>
<link
rel =
"inventory"
uri =
"/Inventory/product/{id}"
/>
</order>
HATEOAS helps frontend developers discover new types, but it’s not widely used. Because REST doesn’t have a formal schema, field-level introspection is not possible.
Although GraphQL and REST do share the same transport protocol (typically HTTP) and often the same data response format (JSON), the two approaches for retrieving data are substantially different and were designed for different use cases. If REST is working for you now, stick with it.
If you need more than REST can provide, consider adopting GraphQL as a complement to REST, not a replacement.
Final Thoughts
In this chapter, we discussed the challenges of REST and why commerce, specifically, requires more than REST on its own. Then, we introduced GraphQL and discussed its pros and cons. Finally, we discussed why you should view GraphQL as a complement to REST, not a replacement for it.
In Chapter 2, we cover the GraphQL specification.
Get GraphQL for Modern Commerce now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.