Insight

GraphQL: Just a Hipster Tech or the Future of API Access?

by Rich Wagner
Date
Apr 15, 2020
Tags

GraphQL has become the hipster way of accessing data from an API web service. Something the cool kids do. GraphQL has been around for a few years: it was originally created by Facebook back in 2012 and then later open sourced in 2015. Yet, as we head deeper into 2020, it continues to gain momentum and is emerging more today than ever as a serious alternative to traditional REST APIs.

You can think of GraphQL as having two components:

  • An intuitive query language for fetching data

  • A runtime that processes and fulfills queries in an app or web site

To implement a GraphQL API, you need to add GraphQL support to both sides of the app. On the server side, the API developer adds a layer that can respond to queries and transpose app data into GraphQL responses. Then, on the client side, the app uses the GraphQL query language to specify what data it wants returned. There are several popular open source libraries available for the client and server to make GraphQL quick and relatively painless to implement.

In this post, I explore ten essentials you need to know to understand what GraphQL is and how it is substantially different in concept and practice from a traditional REST API. I then wrestle with the money question: Is GraphQL a substantively better API approach than REST? Or is it merely a newfangled, hipster way of doing the same thing?

1. A GraphQL API is a Funnel.

A typical REST API will have a multitude of endpoints, structured based on the entities of the app itself. So, for a blog website, if you wanted to get all posts, the call would be something like:

GET /posts

To get all users:

GET /users

To get a specific user:

GET /users/:id

To get a specific post of a specific user:

GET /users/:id/posts/:id

So, for each entity that the API developer wants to expose via REST (such as comments, tags, and categories), you would have corresponding endpoints. As a result, the more complex and expansive that the entities of the app are, the more complex and expansive the set of API endpoints.

In contrast, regardless of how simple or wide-ranging the app data is, GraphQL funnels all queries and operations through a single endpoint:

POST /graphql

That’s it. As such, a GraphQL API requires a lot less repetitive scaffolding code to be developed and maintained on the server side for each new API call.

Unlike REST, the GraphQL endpoint itself implies nothing about the structure or type of data that is being returned. Instead, as I’ll explain next, the specific data that you want to get returned from the API is based on entirely the content of the query that you provide.

2. GraphQL is WYSIWYG.

WYSIWYG is that classic 90’s term that is often used to describe editing software (such as Microsoft Word or a Wordpress blog editor) that allows a document or another form of content to be displayed and edited in a way that closely emulates what the content will look like when printed or displayed on screen.

GraphQL employs a similar what-you-see-is-what-you-get concept when it comes to fetching data. In other words, the JSON that is returned is identical in structure to the query request that is provided. For example, suppose I wanted to get all of the posts in my blog, I could provide the following “JSON-esque” query in GraphQL:

# query keyword is optional for simple queries
query {
    posts {
        # array of post nodes
        edges {
            # represents a post containing the specified fields
            node {
                id
                postId
                title
                date
                slug
                status
                modified
                excerpt
                link
            }
        }
    }
}

(In the code above, note that `#` marks a line comment.)

This GraphQL query serves as a template for the resulting data set, which takes on the same basic construction:

{
  "data": {
    "posts": {
      "edges": [
        {
          "node": {
            "id": "cG9zdDo4Ng==",
            "postId": 86,
            "title": "Attacked by Slack?",
            "date": "2020-02-19T15:53:41",
            "slug": "attacked-by-slack",
            "status": "publish",
            "modified": "2020-02-19 15:53:41",
            "excerpt": "<p>Over the past 5 years, Slack has become a revolutionary collaboration tool for businesses. However, it has also become their oppressor. <\/p>\n",
            "link": "http:\/\/localhost:8080\/2020\/02\/19\/attacked-by-slack\/"
          }
        },
        {
          "node": {
            "id": "cG9zdDo4Mw==",
            "postId": 83,
            "title": "AI vs. Human Copywriters",
            "date": "2020-02-19T15:47:04",
            "slug": "ai-vs-human-copywriters",
            "status": "publish",
            "modified": "2020-02-19 15:47:04",
            "excerpt": "<p>The downside to our new robot writers isn’t job loss or learning that creativity isn’t creative. It’s the horrible things it says about us.<\/p>\n",
            "link": "http:\/\/localhost:8080\/2020\/02\/19\/ai-vs-human-copywriters\/"
          }
        },
        {
          "node": {
            "id": "cG9zdDo3OQ==",
            "postId": 79,
            "title": "On Twitter, the Good Guys Are Also the Bad Guys",
            "date": "2020-02-19T15:42:23",
            "slug": "on-twitter-the-good-guys-are-also-the-bad-guys",
            "status": "publish",
            "modified": "2020-02-19 15:43:04",
            "excerpt": "<p>Twitter is turning us all into disciplinarians, preachers, and politicians instead of communicating in good faith.<\/p>\n",
            "link": "http:\/\/localhost:8080\/2020\/02\/19\/on-twitter-the-good-guys-are-also-the-bad-guys\/"
          }
        }
      ]
    }
  }
}        

3. A GraphQL Query Is Dynamic and Optimized.

A typical REST API has a specific, predefined data structure that is returned for each API endpoint. Consider, for example, the JSON dataset shown above. For a REST API, one could expect that data to be the result of a GET /posts call and be used in such cases as populating a listing of posts. However, suppose you have a dropdown menu that you want to build that uses just the title and link fields. You would probably still call GET /posts to get the posts array and then extract the fields (title and link) that are needed for the narrower context. While that sort of solution is commonly performed using REST APIs, notice that a lot of extra, unwanted data is being returned to the client to populate a simple dropdown menu. The id, postId, date, slug, status, modified, and excerpt fields are all extraneous for this context.

GraphQL solves this problem by allowing you to build dynamic, optimized queries at runtime, tailored exactly to the data you want returned to the client based on your immediate need. Therefore, the query to populate the dropdown menu could be trimmed to just include a bare minimum of fields:


{
    posts {
        edges {
            node {
                # just two fields are needed here
                title
                link
            }
        }
    }
}

Since the resulting JSON takes on the same structure as the GraphQL query, you have just the two essential pieces of info that you need to assemble the dropdown menu. The other fields are not even sent to the client.

4. GraphQL Loves a Mash-Up.

When you are working with a REST API, it is not uncommon to make a series of calls to retrieve the specific dataset that you are looking for. Consider our blog illustration. Suppose you want to get all of the posts from a specific user, and you want each post to include all of its associated tags. To get these multiple levels of data from a REST service, you may need to call GET /users, GET /users/:id/posts, and GET /tags to build the data structure that you want on the client.

In GraphQL, since the format of the JSON result is based on our query, you can compose a query that grabs all of the needed info in one big swoop to avoid multiple requests. For my user post example, the GraphQL query would look something like this:

# query operator is optional, except for named queries and cases in which 
# you want to use a variable
query GET_USER_POSTS($id: ID!) {
	# incoming $id value is used as a query filter
	user(id: $id) {
		id
		name
		posts {
			edges {
				node {
					id
					postId
					title
					date
					slug
					tags {
						nodes {
							id
							name
						}
					}
				}
			}
		}
	}
}

This query demonstrates two items of note. First, the user->posts->tags data construction can be easily combined into one query call. Second, while the nitty gritty details of GraphQL variables are beyond the scope of this post, this query also shows how a variable can be used in a query — in this case, the id of the specified user.

Let me add a second example that demonstrates the mash-up capabilities of GraphQL even more clearly. Suppose you wanted to get all of the post and tag entities using REST. Since post and tag are “sibling” entities, a REST API would almost never have an endpoint that returned both in a data set. As a a result, you would need to call GET /posts to return all posts and then issue a second GET /tags call to return all tags.

GraphQL allows you to combine these sibling data structures into a single JSON result using a query like:

{
	posts {
		# all post nodes are returned in the edges array
		edges {
			node {
				id
				postId
				title
				date
				slug
			}
		}
	}
	tags {
		# all tag nodes are returned in the edges array
		edges {
			node {
				id
				tagId
				name
			}
		}
	}
}

5. GraphQL Can Take Selfies.

A REST API has multiple endpoints, but to know what each of those endpoints are and their respective options, you must be able to see the accompanying documentation of the API to make heads or tails of it. That’s no big deal when the documentation is complete, accurate, and up to date. But when it is not, then a client developer can spend a lot of trial and error time trying to determine what works and what does not.

In contrast, a GraphQL API is “self-documenting” by supporting introspection. To get the full schema available for an API, you can simply issue the following query:

# returns the full schema from the server
{
	__schema {
		types {
			name
			kind
			fields {
				name
				type {
					name
					kind
				}
			}
		}
	}
}

The GraphQL backend returns a JSON object that enumerates the full set of types that can be queried using that service. You can then use this information to determine both the type of queries you want to perform as well as the structure of those queries.

You can also perform introspection on a specific GraphQL type. For example, suppose I wanted to get a list of available fields for the Post entity. I would issue the following query:

# returns full list of fields available for Post
{
	__type(name: "Post") {
		name
		description
		fields {
			name
			type {
				name
				kind
				ofType {
					name
					kind
				}
			}
		}
	}
}

Introspection can be invaluable during the app development process and eliminates the dependency that the client developer has on making sure that the API documentation is up to date and in sync with the actual API itself.

6. GraphQL is (Kinda, Sorta) Versionless.

REST APIs often have a version number denoted as part of their naming scheme (e.g., GET /v1/posts or POST /v2/categories). Version support is essential if underlying data structures ever change over the life of the API; otherwise, you risk breaking the client. Let me explain. Suppose a client app expects a certain structure and set of fields for a post entity, but the API developer modifies the returning dataset due to certain feature enhancements or underlying database changes. Without versioning, the client’s code would break when it receives a response data structure that is different from what it expects. However, when a REST API is versioned, the client developer can have full confidence that as long as it is calling the specific API endpoint that it is written to support, then the code will not break.

By contrast, GraphQL is touted by its authors and proponents as being versionless: the rationale being that you can add new fields or deprecate old ones without breaking existing clients. This gives the client the flexibility to add support for the added fields and remove the deprecated ones over time.

Having said that, it is an overstatement to say that GraphQL removes the need for planned migrations of data structures. There’s lots of things that could be done to break the API (e.g., changing the data type of a field from number to string), and GraphQL does not have anything built-in to safeguard the client from a careless API developer.

In the end, as with REST, GraphQL APIs still require discipline on behalf of the API developer to ensure that nothing in the API breaks.

7. Write to Your Heart’s Content with GraphQL.

While I have focused on how GraphQL can be used to query data from an API, it also supports mutation operations for writing or updating of data. A mutation operation looks similar to a query, but uses the mutation keyword:

mutation CreateTagForPost($post:Post!, $tag: TagInput!) {
 createTag(post: $post, tag: $tag) {
    name
 }
}

This operation will create a new tag and add it to the specified post. The following variables are supplied by the client along with the mutation request to denote the specific post and the tag label to be added:

{
  "post": "cG9zdDo4Ng==", 
  "tag" : {
     "name" : "AEM Development"
  }
}

Mutation operations should always be implemented in combination with an authentication scheme, such as JWT, to ensure only authorized clients are able to write to the backend app server.

8. CRUD! GraphQL Doesn’t Utilize HTTP methods.

REST was designed from the ground up to have a uniform interface. Given that, REST APIs typically utilize HTTP methods (GET, PUT, POST, DELETE) for CRUD (Create, Read, Update, Delete) operations to denote what is being performed: GET and POST fetch data, PUT writes data, and DELETE removes data.

In contrast, GraphQL doesn’t utilize the HTTP verbs PUT or DELETE at all, making all operations — including mutations — a POST (or less commonly, a GET) call.

For a POST request, the GraphQL query is sent in the body as JSON:

POST /graphql? HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{"query":"query GET_USER_POSTS($id: ID!) {\n user(id: $id) {\n  id\n  name\n  posts {\n      edges {\n    node {\n  id\n  postId\n  title\n  date\n  slug\n  tags {\n     nodes {\n  id\n  name\n }\n  }\n  }\n  }\n  }\n }\n}","variables":{"id":"dXNlcjox"},"operationName":"GET_USER_POSTS"}

While less commonly used, you can also use a GET request and provide the query as a URL parameter:

GET /graphql?query={footerMenu{url caption type}} HTTP/1.1
Host: localhost:8080

One side effect of not using HTTP verbs is that naming of mutations will be ad hoc and up to the API developer. For example, the operation to delete a post could be: removePost, deletePost, or even sendPostToTrashBin. As such, you need to introspect the GraphQL API to be sure and know specific mutation operation names.

9. Cache. It Doesn’t Come for Free.

A REST API can utilize HTTP caching as a fundamental part of its caching strategy. In so doing, the API can return headers that provide caching instructions for the data being returned. The benefit to the API consumer is that frequently requested calls can be cached without the client developer needing to do anything special.

But with GraphQL, client requests are all POST (or GET) requests and do not utilize the standard HTTP caching mechanism. As a result, caching needs to be explicitly implemented in the client code, which means more effort on the part of the app developer.

10. GraphQL Makes You More Dependent on Third Parties.

When you build a REST API, the simplistic nature of the API structure itself and query support rarely requires a third party library in order to implement on either the client or the server. It’s typically bare bones stuff.

With its own sophisticated query language, GraphQL is a more complicated solution to implement - and something developers are not going to have the time or inclination to do fully on their own. Fortunately, there are many open source libraries available that can make GraphQL straightforward to implement across a host of language platforms. But, at the same time, one should remember that these third party solutions do make the app developer dependent on the quality of the code and documentation of those libraries.

Heeding Uncle Ben’s Advice

GraphQL offers many modern upgrades over a traditional REST API architecture. You can fetch data through a single endpoint. You can introspect the API to know what can be queried. If handled smartly, you can make incremental tweaks and changes to the result data without resorting to versioned endpoints.

But perhaps what makes GraphQL a gamechanger for web developers is its state-of-the-art query language coupled with its ability to dynamically structure the result data at runtime based on the query being provided. That combination is some powerful stuff.

GraphQL clearly has more to offer developers than just hipster cred. But GraphQL proponents should well remember Uncle Ben’s famous adage to Peter Parker: “With great power comes great responsibility.” In other words, in spite of the technological advantages of GraphQL, the quality of a GraphQL solution ultimately depends on the discipline and care taken by the API developer.


Photo by Toa Heftiba on Unsplash

Rich is an anchor in a sea of technology disruption. A Silicon Valley veteran, Rich relishes the challenge of focusing Maark’s technology resources on the big picture while delivering on the immediate one. Rich manages a SWAT team of experts in the art and craft of technology delivery across a breadth of domains, including digital transformation, product development, systems integration, devops, and SaaS solutions.

Technology
Technology
The Robots are Writing, The Robots are Writing
GTP-3 has a byline in The Guardian and that’s great news for creative writing.
Technology
Where Does the Customer Data Go? A Look at CRMs, CDPs, and DMPs
What’s the difference between Customer Relationship Management systems, Customer Data Platforms, and Data Management Platforms?
Technology
It Takes a Village: Going Enterprise with WordPress
WordPress fills a critical hole in the enterprise CMS landscape, if combined with the appropriate plugins and hosting platform.