For the last few months, I've been spending a lot of time building things with the Contentful GraphQL API. I had dabbled with it before, but having GraphQL become part of our free Community edition motivated me to create some cool new projects.
As I got to know our GraphQL API, I realized that I'd had a few misconceptions about how GraphQL worked. I'd incorrectly assumed that I needed to install a client library to be able to get data. Because the state of GraphQL client libraries is a little rough once you leave the Javascript ecosystem, I thought that using GraphQL wouldn't be that practical in other languages. With time — and teammates that gently pointed out my mistakes — I realized that all I needed to make a GraphQL query was an HTTP request.
To demonstrate how easy it is to use GraphQL while also taking the time to correct my assumptions, let's take a look at how to query some data in a few different languages.
The GraphQL query
Before we can get started, we need a query and an API endpoint to hit. Getting both of those items via Contentful is a breeze. For the API endpoint, we can use Contentful's API Base URL https://graphql.contentful.com
, taken from the GraphQL docs. To find a query we can use GraphiQL, an in-browser GraphQL IDE. Open up https://graphql.contentful.com/content/v1/spaces/{SPACE_ID}/explore?access_token={accessToken}
, replacing the spaceID
and accessToken
with your own.
For this example, I'm using the space from my Serverless SuperHero website. Check out this blog post if you'd like to learn more, but the important part is it's already got some content models and content in the space.
From GraphiQL we can start creating a query. GraphQL is self-documenting, meaning that we can use both the documentation explorer and GraphiQL's built-in autocompletion (brought up via ctrl-space) to create a query. Hit the play button. If the query doesn't have any errors a JSON blob containing the data from your space will show up on the right.
I’ll be using the following query, space id and access token in this example, but feel free to substitute your own if you’d like to try it yourself.
I'll be using the following query, space id and access token in this example, but feel free to substitute your own if you'd like to try it yourself.
spaceID = mt0pmhki5db7
accessToken = 8c7dbd270cb98e83f9d8d57fb8a2ab7bac9d7501905fb013c69995ebf1b2a719
Query =
{
showCollection{
items {
title
firstEpisodeDate
lastEpisodeDate
henshinMp4 {
url
}
}
}
}
~~~
## cURL
To start, let's make a request via [cURL](https://curl.se/). Since cURL is a command-line utility, we can dive into all of our arguments before moving onto programming languages. We have a query, so we can modify the cURL example provided in the [Contentful developer docs](https://www.contentful.com/developers/docs/references/graphql/#/introduction/http-methods/).
~~~bash
curl -g \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 8c7dbd270cb98e83f9d8d57fb8a2ab7bac9d7501905fb013c69995ebf1b2a719" \
-d '{"query":"query{showCollection {items { title firstEpisodeDate lastEpisodeDate henshinMp4 { url }}}}"}' \
[https://graphql.contentful.com/content/v1/spaces/mt0pmhki5db7](https://graphql.contentful.com/content/v1/spaces/mt0pmhki5db7~~~)
Even though the Contentful GQL API supports both POST and GET, we'll be using POST exclusively in this blog post, because POST enables us to send the query as a part of the JSON payload. If you’re using your own query, space id and accessToken, go ahead and replace them.
When that command is run in a terminal it will output a JSON blob matching that of our previous example! Every one of our examples following this will use a similar format to the cURL request we made.
Python
There are few different ways to make an HTTP request in Python, which gives us options in how we'd make a GraphQL query. I'm a fan of the requests library (https://requests.readthedocs.io). It's extremely simple although, unlike urllib, we will have to install it.
Since this is a new Python project, spin up a new virtual environment, install requests and create a new Python file.
~~~bash
create the virtual environment
virtualenv env
activate the environment
source env/bin/activate
install the requests library
pip install requests
create an empty file.
touch request.py
In your newly created Python file, import the libraries. Set the space id and access token. I'd normally have this in a .env file. But, since this is an example, we can save it in the file.
Create your endpoint URL and Authorization headers. I'm a fan of f-strings — the new way to do string manipulation in Python — so I'm using that format. Set the query and fire off the request. At the end, if we get a 200 HTTP status code, use the JSON library to format the output.
~~~python
import requests
import JSON
spaceID = "mt0pmhki5db7"
accessToken = "8c7dbd270cb98e83f9d8d57fb8a2ab7bac9d7501905fb013c69995ebf1b2a719"
endpoint = _f_"https://graphql.contentful.com/content/v1/spaces/{spaceID}"
headers = {"Authorization": _f_"Bearer {accessToken}"}
query = """query {
showCollection{
items {
title
firstEpisodeDate
lastEpisodeDate
henshinMp4 {
url
}
}
}
}"""
r = requests.post(endpoint, _json_={"query": query}, _headers_=headers)
if r.status_code == 200:
print(json.dumps(r.json(), _indent_=2))
else:
raise _Exception_(_f_"Query failed to run with a {r.status_code}.")
Run the code with python request.py
and you'll see a JSON blob!
JavaScript (Node.js)
Similarly to the Python instructions, we'll be making use of an HTTP library for JavaScript. Since the JavaScript version of requests is no longer supported, we'll take advantage of got. In a new directory, run npm install got
. Create a new request.js
file with the following code.
~~~javascript
const got = require("got");
const spaceID = "mt0pmhki5db7";
const accessToken = "8c7dbd270cb98e83f9d8d57fb8a2ab7bac9d7501905fb013c69995ebf1b2a719";
const endpoint = "https://graphql.contentful.com/content/v1/spaces/" + spaceID;
const query = query{
;
showCollection{
items {
title
firstEpisodeDate
lastEpisodeDate
henshinMp4 {
url
}
}
}
}
const options = {
headers: {
Authorization: "Bearer " + accessToken,
"Content-Type": "application/json",
},
body: JSON.stringify({ query }),
};
got
.post(endpoint, options)
.then((response) => {
console.log(response.body);
})
.catch((error) => {
console.log(error);
});
![Screenshot of successful JS request](https://images.ctfassets.net/fo9twyrwpveg/3xbzQZrFWIR55jScqoIeEx/5147f0a898070cdcabcca540cba4f82a/Screen_Shot_2020-12-14_at_3.23.04_PM.png?q=90)
We can run this example with `node request.js` to get our JSON blob.
## Ruby
Making HTTP requests with Ruby is something that we can do without installing libraries. Everything we'll use in the following example is already built into Ruby. Create a new `requests.rb` file with the following code.
~~~rb
require 'net/http'
require 'json'
require 'uri'
spaceID = "mt0pmhki5db7"
accessToken = "8c7dbd270cb98e83f9d8d57fb8a2ab7bac9d7501905fb013c69995ebf1b2a719"
query = """query {
showCollection{
items {
title
firstEpisodeDate
lastEpisodeDate
henshinMp4 {
url
}
}
}
}"""
uri = URI("https://graphql.contentful.com/content/v1/spaces/#{spaceID}")
res = _Net_::_HTTP_.start(uri.host, uri.port, use_ssl: true) do |http|
req = _Net_::_HTTP_::_Post_.new(uri)
req['Content-Type'] = 'application/json'
req['Authorization'] = "Bearer #{accessToken}"
# The body needs to be a JSON string.
req.body = _JSON_[{'query' => query}]
puts(req.body)
http.request(req)
end
puts(res.body)
We can run this file with ruby requests.rb
.
PHP
Similar to Ruby, we don’t need to install any libraries for PHP. Like the previous example, append your space ID onto the Contentful GraphQL endpoint, and set up the headers and request body. To wrap things up, make the POST Request. If you get a response without any errors, log the output.
~~~PHP
<php
$spaceID = "mt0pmhki5db7";
$accessToken = "8c7dbd270cb98e83f9d8d57fb8a2ab7bac9d7501905fb013c69995ebf1b2a719";
$endpoint = "https://graphql.contentful.com/content/v1/spaces/%s";
$query = "query {
showCollection{
items {
title
firstEpisodeDate
lastEpisodeDate
henshinMp4 {
url
}
}
}
}";
$data = array ('query' => $query);
$data = http_build_query($data);
$options = array(
'http' => array(
'header' => sprintf("Authorization: Bearer %s",$accessToken),
'method' => 'POST',
'content' => $data
)
);
$context = stream_context_create($options);
$result = file_get_contents(sprintf($endpoint, $spaceID), false, $context);
if ($result === FALSE) { /* Handle error */ }
var_dump($result);
Go ahead and run this with PHP request.php to get our final JSON blob.
Wrap up
We’ve made a GraphQL request over HTTP in five different ways (cURL, Python, Javascript, Ruby and PHP). If we really wanted to, we could simplify these requests even further by dropping the requests library from the Python or JavaScript examples, choosing to just use the built-in url processing capabilities of the languages.
These examples show how consuming GraphQL isn’t harder than a traditional restful API, but its benefits are far greater than Rest’s. By taking advantage of GraphQL, we’re able to get only the data that we requested in a single query. To make the calls that we used in our query example, we would have to make multiple requests to the Contentful Rest API. The response is more predictable, and we’re not bogged down with additional information that we might not need.
Hopefully these examples can inspire you to dive deeper into this new way of querying data. If you're interested in learning more, check out our GraphQL course. In it, Stefan from our DevRel team will walk you through how to get started with GraphQL, React and Contentful covering GraphQL tooling, fragments, directives, variables, query complexity costs (and much more).