Headless API
Contents
Now that we’ve created some content, let’s see how we can access and use it in via API - headless style. This chapter introduces Guillotine, the app that’s responsible for automatically generating GraphQL APIs for your content.
Enonic XP is very flexible, and provides multiple ways of exposing and using content. If you are not interested in accessing content via the standard headless API, feel free to skip this chapter and the sections mentioning "Headless" in the rest of the tutorial. |
You won’t be building anything new in this {chapter}, but we use the data set and everything that you’ve created over the previous chapters. |
Installing Guillotine
The first thing you need to do in order to get going is installing the Guillotine application. It is available on Enonic Market.
Fortunately, you can install Guillotine directly from your Enonic XP instance’s admin console.
-
From the XP menu (top right), choose
Applications
. -
Click Install from the top menu
-
Search for
Guillotine
, and click Install`.
That’s it! 🎉
Query playground
Back in Content Studio, you should now have access to a new item called Query playground
in the left hand menu. Open it, and you should see this:

Query playground is an interactive GraphQL API explorer. You enter your query in the text area to your left and when you execute it, you’ll get the results of the query on the other side of the screen.
The "Docs" tab on the right-hand side can be used to see all details of the underlying GraphQL schema.
Guillotine queries
GraphQL is a graph "query language". It allows you to create highly specific queries for exactly the content that you need (visit the official GraphQL docs to learn more).
The API explorer offers autocompletion for fields. You can use this to interactively explore the API. You can also open the schema and documentation from the right-hand side menu tabs. |
To get started, copy the below query into your the query playground (left hand panel) and run it by clicking the play button.
You can also run queries by using the keyboard shortcut ctrl+enter/⌘+enter . |
{
guillotine {
getChildren {
displayName
_path
_id
}
}
}
The response is returned as JSON. In this case, it will contain the contain the site (My First Site), and your images folder. For each item you will also see it’s name, and location within the project structure.
{
"data": {
"guillotine": {
"getChildren": [
{
"displayName": "Images",
"_path": "/images"
},
{
"displayName": "My first site",
"_path": "/my-first-site"
}
]
}
}
}
{
guillotine {
getChildren(key: "/my-first-site") {
displayName
_path
}
}
}
The response will contain the immediate children of the site: the "artist" folder we created and the "Templates" folder that was already there.
{
"data": {
"guillotine": {
"getChildren": [
{
"displayName": "artists",
"_path": "/my-first-site/artists"
},
{
"displayName": "Templates",
"_path": "/my-first-site/templates"
}
]
}
}
}
Site Context
Throughout this tutorial we will primarily be working in the context of this site - for this specific purpose, Guillotine supports a so-called site context, which will simplify your queries.
Open the "Request headers" section of Query playground, and paste the following configuration:
{ "X-Guillotine-SiteKey": "/my-first-site" }
The SiteKey, may either be the site’s path, or unique identifier. Using the ID is safe as you don’t need to know the exact location of the site within your structure. |
When executing queries, you now have a few new tricks available, as you will see in the examples below
Artist queries
Let’s look at writing slightly more complex queries that get us information about the artists we’ve created. To start with, let’s see if we can just fetch all the artists that we created.
This time, we will use the ${site} placeholder to specify the path
{
guillotine {
getChildren(key: "${site}/artists")
{
displayName
}
}
}
The result of this query should list all the artists you created previously:
{
"data": {
"guillotine": {
"getChildren": [
{
"displayName": "P!nk"
},
{
"displayName": "Missy Elliott"
},
{
"displayName": "Cardi B"
}
]
}
}
}
Neat! Next, let’s try a slightly more complicated query. In this one, we’ll also get the data inside the content nodes and extract their names and alias fields. Because all content shares the displayName
property, you can query for it without specifying what type it belongs to. However, to get fields that exist on the Artist type, you need to specifically tell GraphQL what type you’re looking for via inline fragments. Inline fragments start with … on
and then specify the content type you want.
Learn more about inline fragments at the GraphQL documentation’s Inline Fragments section |
{
guillotine {
getChildren(key: "${site}/artists/")
{
displayName
... on com_example_myproject_Artist { (1)
data {
name
about
}
}
}
}
}
1 | This inline fragment lets us extract extra data from content of the Artist type. |
Execute this and your response should look a little something like this:
{
"data": {
"guillotine": {
"getChildren": [
{
"displayName": "Missy Elliott",
"data": {
"name": "Melissa Arnette Elliott",
"about": "Melissa Arnette Elliott (born July 1, 1971) is an American rapper, singer, songwriter, and record producer."
}
},
{
"displayName": "P!nk",
"data": {
"name": "Alecia Beth Moore",
"about": "Alecia Beth Moore (born September 8, 1979), known professionally as Pink (stylized as P!nk), is an American singer and songwriter."
}
},
{
"displayName": "Cardi B",
"data": {
"name": "Belcalis Marlenis Almánzar",
"about": "Belcalis Marlenis Almánzar (born October 11, 1992), known professionally as Cardi B, is an American rapper and songwriter."
}
}
]
}
}
}
Advanced queries
The query
field underneath guillotine
allows you to search the system for what you want. This field accepts a number of optional, named arguments to help you hone in on exactly what you’re looking for. These arguments are:
-
contentTypes
-
The content types we’re looking for. Can either be a lone string if you’re looking for a single content type, or a list if you want to search among multiple types.
-
filters
-
A list of filters to apply to the results after the query has been executed. We won’t get into them here, so if you want to learn more about them, head to the filters documentation.
-
first
-
Specify how many nodes you want at most in your result. If your query yields a list of results, you can specify that you only want the first
n
results, wheren
is a positive integer. Works well in concert with offset. -
offset
-
Specify how many results you want to cut from the beginning of the list of results. Most commonly used for pagination.
-
query
-
A NoQL query string.
-
sort
-
A NoQL sort string.
For instance, using the data set we’ve created previously in this guide, we could run a query like this:
{
guillotine {
query(
first: 2,
offset: 0,
contentTypes:
[ "com.example.myproject:artist", (1)
"com.example.myproject:animal" ],
sort: "displayName desc") {
displayName
contentType {
displayName
}
}
}
}
1 | To query for multiple types, enclose them in square brackets and separate them with commas. |
If a content type doesn’t exist, such as in the above query, GraphQL will process the query as normal: it just won’t find any results of that type. |
This query would produce a result like the following:
{
"data": {
"guillotine": {
"query": [
{
"displayName": "P!nk",
"contentType": {
"displayName": "Artist"
}
},
{
"displayName": "Missy Elliott",
"contentType": {
"displayName": "Artist"
}
}
]
}
}
}
To perform a more advanced query, you can use the query
parameter. The following GraphQL query uses the n-gram query functionality to find all the artists that have a term (part of a text) that starts with rap
somewhere in their data.
{
guillotine {
query(
contentTypes: "com.example.myproject:artist"
query: "ngram('_allText', 'rap')"
sort: "displayName asc") {
displayName
}
}
}
The result would look something like this:
{
"data": {
"guillotine": {
"query": [
{
"displayName": "Cardi B"
},
{
"displayName": "Missy Elliott"
}
]
}
}
}
Naturally, you can also use the standard boolean operators to combine or negate clauses. If you wanted to find all artists that mention both rapping and singing, you could try a query like this:
{
guillotine {
query(
contentTypes: "com.example.myproject:artist"
query: "ngram('_allText', 'rap') AND ngram('_allText', 'sing')"
sort: "displayName asc") {
displayName
}
}
}
And find out that with our data set, there’s only one:
{
"data": {
"guillotine": {
"query": [
{
"displayName": "Missy Elliott"
}
]
}
}
}
Learn more
For a detailed overview of the query language and an extensive example selection, the NoQL reference is the place to go. For more query examples and information about how to get up and running with Guillotine, you can check out the headless CMS intro guide or the Guillotine docs.
If you want to learn more about GraphQL in general, the Introduction to GraphQL documentation is a good place to start.