If you've been developing against APIs for more than just a few years, I'm willing to bet that you've encountered and used RESTful-style APIs. Why am I so confident? Because REST has become the de-facto standard, most commonly used style of API on the web today. Even though REST is great and has become ubiquitous, it's not without a few challenges and shortcomings:
- Under-fetching data you need
- Over-fetching data you don't need
- Many endpoints - usually 1 for each resource type
- Related data requires multiple calls
- Difficult discovery and auto-tooling
- ...and more
To solve these and other issues, GraphQL was developed by Facebook starting in 2012. It was originally internal-only, but was open-sourced in 2015 and since then it has gained immense popularity. A recent study indicated that it is used in nearly 20% of all organizations with an API. This may not sound like a lot—especially when compared to REST's roughly 82% usage rate—but another study shows that developers' experience with GraphQL grew from around 5% in 2016 to nearly 40% in 2019. That's some solid growth, and Anvil has contributed to those statistics. While we have some RESTful APIs, we rely heavily on GraphQL for the majority of our exposed capabilities.
Yet despite the significant and rapidly growing share of GraphQL APIs out there, some developers who are used to RESTful APIs have yet to use it, and some are reluctant to try and learn it. I'm here to show you that consuming a GraphQL API is really nothing to be afraid of, and it’s something that any developer can figure out the basics of.
GraphQL Background / Primer
First off, if you have no idea what GraphQL is about and what it feels like, I'd like to suggest a few things:
- Read this article to help gain a reasonable background on what GraphQL is all about.
- Try playing around with some live queries in this online demo to get the feel for how things actually work. Take note of how you can traverse the relationship graph of objects from the root object, retrieving nested/related objects of your choice. Also notice how you can request as much or as little information as you want from each of those objects.
Hopefully after familiarizing yourself with some basics of interacting with a GraphQL API, you realize that for simple queries it's very straightforward. Here's a quick cheat sheet on what you need to know:
- Instead of multiple "endpoints" that you might have in a REST API, a GraphQL API has just a single endpoint, but additionally defines and exposes any number of Queries (for read operations) and Mutations (for insert/update operations). These operations are hopefully given helpful, descriptive names like
invoices
orupdateUser
, etc. - GraphQL is all about Types, and each operation will normally return an object of a specific Type. These Types include some basic Scalars like
Int
,String
,Boolean
, but custom Types built from scalars or even other custom Types can also be defined - thinkUser
,Organization
,Invoice
, etc. These custom Types typically are objects that contain 1 or more Fields, each of which has a Type. - Field definitions not only require a Type specifier, but they also can have a few modifiers:
!
: A non-nullable (i.e. "required") value can be indicated with a!
. E.g.name: String!
.[<Type>]
: An array of values can be indicated by surrounding a Type in brackets. E.g.names: [String]
.- These 2 modifiers can be combined arbitrarily, for example to indicate "an array of non-nullable Strings" (e.g.
names: [String!]
). Or to indicate "an array of non-nullable Strings, which itself cannot be null" (e.g.names: [String!]!
).
- The last big thing to mention is that GraphQL supports Arguments for Queries (e.g. the
invoice
query expects aninvoiceId
argument), Mutations (e.g. thecreateUser
mutation expectsname
andemail
arguments) and also Fields (e.g. theaddresses
field on aUser
type may accept acountry
argument for filtering).
And with that, while there are definitely more advanced things one can learn about GraphQL, you should be armed with enough knowledge about the GraphQL spec to understand how to use and consume most GraphQL APIs.
Request Shape
Hopefully by now you're no longer mystified by interacting with a GraphQL API, and are starting to see some of the advantages of it. All that's left to do now is actually write some code that consumes one! GraphQL is so popular that there are client packages available in every popular language like Node, Python, Ruby, Java, Go, and more. Chances are that whatever language you're using to consume REST APIs has a library to make it easy to consume GraphQL APIs.
If you're unable (or unwilling) to add more dependencies to your code base, or you'd just like to "roll your own" client for whatever reason, I'll show you some examples of how easy it is a bit later on. What I hope you'll realize is that at the end of the day, a GraphQL API call is just a POST
request to the server's GraphQL endpoint with a JSON payload consisting of a few key/values:
query
: A string containing the query or mutation you'd like to execute.variables
: A JSON object whose keys match the name of any variables used in yourquery
, and whose values are the values for the server to use for those variables.
Most servers will even let you send those key/values as query params to a GET
request if you really want to for some reason.
The response should come with a 200
/OK
status (even when there are errors), with a content-type
of application/json
and have the following JSON structure:
errors
: An array that should be present and populated only if there were errors encountered. The elements in the array should provide details about what thing or things went wrong. As noted above, the server response will still be200
/OK
even if there are errors.data
: A JSON object containing the result of your query or mutation operation.
Sample Code
Hopefully this sounds easy so far, but let's see how it looks in code. Let's say we have a relatively simple User
query that looks like this:
query User($id: Int!) {
user(id: $id) {
id
email
}
}
And let's say that the $id
variable will be the integer 42
.
The expected response would look like this:
{
"data": {
"user": {
"id": 123,
"email": "foo@bar.com"
}
}
}
Here are some examples of how to execute this query in different languages - I've left out the authorization header for simplicity:
curl
curl -X POST 'https://example.com/graphql' \
-H 'Content-Type: application/json' \
-d '{"query":"query User(id: Int!) { user(id: $id) {id email} }", "variables": {"id": 42}}'
python
# Every project will have a dependency like this if it's making HTTP requests
import requests
import json
data = {
'query': 'query User(id: Int!) { user(id: $id) {id email} }',
'variables': { 'id': 42 }
}
headers = { 'Content-Type': 'application/json' }
body = requests.post(
'https://example.com/graphql',
data=json.dumps(data),
headers=headers
).json()
Node
/JavaScript
// Every project will have a dependency like this if it's making HTTP requests
import fetch from 'isomorphic-fetch'
const res = await fetch('https://example.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: '{"query":"query User(id: Int!) { user(id: $id) {id email} }", "variables": {"id": 42}}',
})
const body = await res.json()
Ruby
require 'uri'
require 'net/http'
require 'json'
uri = URI('http://localhost:3000/graphql')
res = Net::HTTP.post_form(uri, 'query' => 'query {currentUser{id email}}', 'variables' => { 'id' => 42 }.to_json)
body = res.body
PHP
$url = 'http://localhost:3000/graphql';
$data = array('query' => 'query {currentUser{id email}}', 'variables' => array('id' => 42));
$options = array(
'http' => array(
'header' => "Content-Type: application/json",
'method' => 'POST',
'content' => json_encode($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, FALSE, $context);
Summary
So there you have it! In my opinion, there's nothing mystical or particularly challenging to using GraphQL APIs - they're easy, efficient, and pretty fun/awesome. Admittedly, I kept the examples rather simple, and there are far more complex things that can be done with GraphQL (for example, trying to upload binary files can be a bit tricky), but for many APIs this is more or less all you need to know. So don't be afraid when you hear that an API is GraphQL - it's only some basic REST-like calls under the covers. Happy coding!