REST APIs are a common standard for modern applications. The strategy and tactics for building APIs using REST are well defined, and all sorts of companies offer REST APIs for consumers, partners, and clients. REST APIs power some of the most used applications on the planet. Developers like having explicit URIs that use standardized “VERBS” for operational procedures. In the wild west of distributed systems, REST provides some clarity and needed structure. What would it be like if each company designed its own APIs, API signatures, and operation methods?
As REST has grown in popularity over the last 20 years, some organizations have experienced friction. While REST is terrific, it does have some challenges. One challenge is the data returned from a REST call is standardized. You may be reading this and wonder why standardized data return payloads would be a challenge. Still, if you’ve ever worked with high-scale internet applications, you can imagine how much data is being processed and sent over the wire and not being used by the end client.
The Importance of Payload Size
If you operate a small to medium scale web operation, you may not notice this, but there is a bottleneck in all systems. If you do enough optimization, you will eventually find sending unneeded data to the client millions of times is the current, most relevant bottleneck blocking you from scaling.
Where API Versioning Can Hurt
Another challenge that happens in REST is the creation of “New and Improved V2” APIs. A set of APIs may eventually be versioned where the old APIs are still available alongside the new APIs, just with a version indicator somewhere in the URI. It’s essential to maintain URIs once they are published because applications directly refer to the URIs in the software code.
It’s not always possible to update all places that have these URIs. In particular, mobile applications are especially problematic to adjust because the publishing and deployment timeline is partially in the hands of a 3rd party, namely the Google Play or Apple App store. Thus, you have the problem of needing to ship new APIs and maintain URI integrity for existing applications. This condition puts additional work on the development team to allow two different worlds (API versions) to coexist without causing more significant problems.
GraphQL has an answer to both of these problems. In a recent React Wednesday, GraphQL expert, mentor, and trainer Eve Porcello gave TJ VanToll and me a much-needed master class in GraphQL. She demonstrated how GraphQL works, how to adjust the returned data, how to handle security and authentication, and how to push updates to subscribers. By the end of the show, I’ve become a bit excited by the possibilities and capabilities of GraphQL and can’t wait to use it in my applications.
What is GraphQL, and Why Should You Care?
GraphQL offers many of the same benefits as REST and avoids some of the drawbacks. Let’s look at some areas where GraphQL differs from REST and discuss how this helps.
One Endpoint versus Many
Rather than think of data interactions as a collection of unique URIs and calls, GraphQL asks you to pipeline all of your requests through a single endpoint. This endpoint is a durable reference for all GraphQL enabled interactions with your back-end layers. The signature you use to call the endpoint relates to the Schema of your data that you define.
In REST, you’d have many endpoints, each relating to a particular concept or data collection on which the requestor can perform operations. At certain levels of complexity, the explosion of endpoints to maintain and manage creates problems for teams to solve.
To Schema or Not to Schema, That Is the Question
Software developers and architects use many different tools to document a catalog of APIs. In general, the idea is that the API endpoint is a stand-alone entity. The API for Products is encapsulated from the API for Customers. While this is a great way to define services, often the applications that need data think of the data combined with other data.
This disconnection between the provided manner for data access and how applications make use of the data creates conceptual friction. There is a tendency for API developers to feel like their work ends when there are good APIs for the data, collections, and permissible operations.
In a GraphQL environment, the GraphQL developers own and maintain a schema. The Schema is a blueprint for the defined types, and their hierarch. A data collection can have a dependency on another data collection. The schema models out all of the eligible data sets available in the GraphQL environment. Schemas can be auto-generated from data sources or configured by hand. Autogeneration is fast and easy to do, though Eve prefers focusing the Schema on the data usage patterns and being thoughtful during the creation process.
By necessity, the GraphQL team works closely with front-end developers and data consumers to help them get the data they need most efficiently. In turn, the GraphQL team gets visibility into data consumption patterns and emerging needs so they can continually evolve and optimize the Schema efficiently. By the way, there are several nice, freely available tools to manage GraphQL schema.
Controlling the Data Payload
It is common in a GraphQL environment to make a single call that returns compound data from different collections and business types. Think of GraphQL as a fetcher and assembler of the right data document you need at that time. Applications consuming GraphQL data no longer need to call multiple endpoints and assemble the data on the client-side. That processing and software code moves to the GraphQL server.
Controlling Access with Roles and Permissions
Remember, GraphQL pipelines all calls through a single endpoint. Once the network request reaches the GraphQL endpoint, the control passes over to the custom code zone. The endpoint is where control passes to the GraphQL Resolver layer. A resolver is a function that is programmed to fetch the data matching the Type configured in the Schema. You program your Resolver functions with authentication checks, role validations, data access (even REST calls), and all other implementation details required to service the request. The end-user application would invoke the resolver by hitting the GraphQL endpoint with a specific call signature.
Controlling Data Structure with Types
GraphQL comes with several primitive types like String, Int, Float, Boolean, and ID. You can combine these primitive types into complex types that model specific business concepts, like User. Complex types are composable from other complex types, so you’ll be able to model out your data however it makes sense. The GraphQL schema registers which Type should be returned by each resolver function. Before fulfilling the request, the GraphQL server will perform type validation to ensure the returned data matches the expected Type; else, it’ll send back a descriptive error to the calling application.
Where to Get a GraphQL Server
Eve demonstrated the Apollo Server, an open-source, spec-compliant GraphQL server that’ll work with any spec-compliant, GraphQL client. At its core, the Apollo Server is a node server with extra layers implementing the GraphQL specification. There are simple, well-documented instructions to set up your first Apollo GraphQL server, define a schema, and make your first query. If you’d like to get your hands dirty, follow the Apollo server getting started guide.