API Design, Part 3: GraphQL and the Future

Reading Time: 8 minutes

This is part three in a three part series about the history of API design.

  1. Before There Was REST
  2. The Arrival of REST 
  3. GraphQL and the Future

In the last post, we talked about the impact of REST on the API protocol landscape in the early 2000’s. Then something happened in 2015 that I keep hearing about from people who have no idea what API design looked like before 2000. Strap in people.

Let’s Address GraphQL for a Second

GraphQL is a library released by Facebook in 2015 that calls itself a “dynamic API query language.” I meet a lot of unquestioning supporters who think it’s time to ditch REST and write everything in this.

What does this flashy GraphQL tool do?

It allows you to hit one endpoint with one HTTP verb and various request bodies to get what you want.

Does that sound familiar?

It should if you’ve been reading this series, because that is SOAP.

The creator of GraphQL admits this. During his presentation on the library at a Facebook internal conference, an audience member asked him about the difference between GraphQL and SOAP. His response: SOAP requires XML. GraphQL defaults to JSON—though you can use XML.

Others who have endeavored to list the differences between GraphQL and SOAP have also coalesced around the data transfer format as the major difference. Unless you set GraphQL to XML, in which case…it’s SOAP. Same product, different brand.

This is what I mean when I blather on about the importance of context in forming opinions. People get on their soapbox (no pun intended) about how GraphQL is a new paradigm. They don’t know that fuddy-duddy old school companies that they think of as “outdated” were doing this exact thing two decades before they hopped on the bandwagon.

We know some things about the utility of GraphQL if we know that SOAP exists. We can make more informed decisions about when it fits our solution and when we’re better off choosing something else. We don’t have to rebuild all that context.

Comic courtesy of Wulff & Morgenthaler, authors of Wumo (wumo.com)

So what do these protocols mean for building web requests today?

We’ve talked about the strengths, limitations, and use cases of a few different web request protocols. Now that we have that context, we can make informed choices about which protocol to follow.

Or, if we want, we can wield that context to build our own requests using the best-fitting features from existing protocols.

Suppose, for example, that we are writing a custom HTTP API to serve data to a client app for a health insurance company. The API gives us access to all of the members, and at different points the client app needs different combinations of information about the members. A RESTful request would allow us to make a list endpoint returning all members (maybe with query param filters), and it would allow us to make a detail endpoint with more information about any one member. It sounds good, and then we might run into some issues.

Common issues:

  • Query parameters get unwieldy if you’re using a bunch of them at once. For example, a mentor of mine worked on an API in which the request didn’t work because the cacophony of query parameters included in the request exceeded the URL length limit.
  • Response bodies get unwieldy. Often a list endpoint contains a few key data about each list item, and the detail call gives you more. But what if you need a little more than the key data about a lot of items at once? Do you make an individual detail call for every item in the list, just to use a few more fields on the item? Or do you add those fields to the list response…even though, in a long list, they raise the size of an already huge payload? Then to deal with the payload size, do you paginate?

I have seen these questions arise on a number of REST apps. The solution often ends up being some kind of custom endpoint that approximates REST, but doesn’t really fit the Roy Fielding chart. This is OK, mind you. But it’s helpful to have alternatives to REST in mind in case they provide a better solution.

In this situation, maybe the SOAP/GraphQL methodology can help us. We can make one endpoint to get the members and include details in the request payload about how we want them to come back. This is a fantastic option for doing highly customized responses at scale, and I have a hunch that this is why SOAP sticks around at huge companies. When your database table would cover a football field in 8 point font, vanilla REST lacks the nuance to help you pull out exactly what you need.

If we go full SOAP, though, we miss out on some nice things:

  • SOAP requests don’t have a Roy Fielding chart that explain their common use cases. So the request envelopes, fully customized to the company, don’t benefit from the herd documentation of a community standard. In order for folks to know how the requests will work, they need detailed documentation.
  • Our SOAP request would have one endpoint to which we would submit a request body, and that request body would contain information both about which records we need (filtering) and about which attributes we need from each record (response sculpting).

That’s fine, honestly. It’s not the literal worst. But wouldn’t it be nice if we could separate our filtering from our response sculpting in the request? If we combine the ideas of REST-style query parameters and SOAP-style request bodies in a single get request, we can.

In a Rails app, we might send such a request like this:


require 'uri'
require 'net/http'
class MemberService
def get_members(filters=Hash.new)
query_params = filters.to_query
uri = URI("https://bertrandshealthinsurance.com/members?#{query_params}")
request = Net::HTTP::Get.new(uri.path, {'Content-Type' => 'application/json'})
request.body = json_options: {
only: [:name, :date_of_birth],
include: {
dependents: {
only: [:name, :date_of_birth]
}
}
}.to_json
response = http.request(request)
end
end

So our .get_members method takes any filters we wish to apply to our members (maybe plan type or employer name). We convert those into query parameters on the URL. Then, in the request body, we include some information about how we want the response to come back: out of all the attributes on each member, we want only their name and birthdate. Also, we want them to come along with their dependents—but only each dependent’s name and birthdate. So now we won’t get ancillary information like membership start date, primary care provider, et cetera bloating our response.

Let’s look at the controller of the receiving app—also written, for this example, in Rails:


class MembersController < ApplicationController
def index
@members = member_params.any? ? Member.where(member_params) : Member.all
json_options = params.fetch(:json_options, {})
render json: @members.as_json(HashWithIndifferentAccess.new(json_options.permit!))
end
def member_params
params.permit(:member_id, :employer_id, :plan_id)
end
end

It just so happens that the json we sent in our request body exactly matches the expected argument to the as_json method in Rails, so we can send it in on line 8 of the controller and trust Rails to map that to the appropriate database information.

Now, this is a hybrid approach—not a “standard” discoverable approach—so developers programming against this are unlikely to guess it. Therefore it needs to be well documented. Luckily, the fact that we have a single internal client means that that risk doesn’t play as prominently in our risk profile as it would for, say, an open-source API with ten thousand stars and followers.

This is just one example to make a broader point: it is worthwhile for us as technologists to cultivate knowledge of the context surrounding our tools so we can make informed decisions about when and how to use them. In this case, we’ve managed to break down some web request protocols and build their pieces back up into a hybrid version that suits our needs.

Conclusion

There are decades of history and a broad cast of characters behind the web requests you know and love—as well as the ones that you might have never heard of. Information first traveled across the internet in 1969, followed by a lot of research in the ’70s, then private networks in the ’80s, then public networks in the ’90s. We got CORBA in 1991, followed by SOAP in 1999, followed by REST around 2003. GraphQL reimagined SOAP, but with JSON, around 2015. This all sounds like a history class fact sheet, but it’s valuable context for building our own web apps.

When we understand where technology comes from, we can more effectively engage with its strengths, limitations, and use cases. We can also pick and choose the elements from that technology that we would like to carry into the solutions we build today.

As technologists, we shape the future. As we do so, it’s valuable for us to dig into how we got here from the past.

If you liked this post, you might also like this stuff I wrote:

Streamlining your automated test strategy with risk maps

What growing your tech team means for your app

Leading a software rewrite at your company

One comment

  1. Probably one of the biggest options provided if you have multiple resources served from multiple endpoints, stitched together by hypermedia, is the ability to evolve from a single monolithic service to a microservice-based approach WITHOUT BREAKING CLIENTS.

    Think about it. Each resource is served from its own endpoint. These URIs are found by looking inside hypermedia and not hardcoding them into clients. So if relations are kept the same, it becomes possible to pull that Ordering System apart from the Employee System, and run 10,000 copies, while only running 500 copies of the Employee System.

    And clients don’t have to know if they are using hypermedia to find things.

    People like to bicker about sending too much/too little data in REST services, but is that really the issue it is? I have yet to see the studies showing people wasting bandwidth. (Probably because MORE bandwidth is wasted on serving up JavaScript libraries!)

    So before we jump into a less-than-cacheable tactic of GraphQL, aka Yet Another Query Language (seriously, we have query languages. Just put them in your client if you need it!), let’s at least use REST correctly and not get hung up on pretty URIs and HTTP verbs. 🙂

    P.S. Good articles. Moarrr!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.