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.

  1. From the XP menu (top right), choose Applications.

  2. Click Install from the top menu

  3. Search for Guillotine, and click Install`.

    Install Guillotine application dialog

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:

API browser integrated in Content Studio

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 .
A GraphQL query getting data about the root items in the project
{
  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.

The site info response
{
  "data": {
    "guillotine": {
      "getChildren": [
        {
          "displayName": "Images",
          "_path": "/images"
        },
        {
          "displayName": "My first site",
          "_path": "/my-first-site"
        }
      ]
    }
  }
}
To fetch the children of the site, try this
{
  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.

The site info response
{
  "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

A GraphQL query fetching all content below the "My First Site" site
{
  guillotine {
    getChildren(key: "${site}/artists")
    {
      displayName
    }
  }
}

The result of this query should list all the artists you created previously:

The result of the above query; all content nodes below "artists"
{
  "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
A GraphQL query that fetches all artists and their data using inline fragments
{
  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:

Artists and their aliases as returned by GraphQL
{
  "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, where n 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:

A Guillotine query using some of the arguments mentioned.
{
  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:

Guillotine query result
{
  "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.

A Guillotine query using nGram search functionality
{
  guillotine {
    query(
      contentTypes: "com.example.myproject:artist"
      query: "ngram('_allText', 'rap')"
      sort: "displayName asc") {
      displayName
    }
  }
}

The result would look something like this:

Rap results
{
  "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:

Querying for artists who both rap and sing
{
  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:

Rappers and singers
{
  "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.


Contents