Get started with Headless CMS
Contents
Get started with Headless CMS
This guide provides a step-by-step hands on introduction to using Enonic XP in Headless CMS mode.
This guide requires Enonic XP 7 |
Introduction
Throughout this tutorial you will learn how to:
-
create a new project using the "Headless Starter"
-
install and use "Content Studio" for editing content
-
customize your application with new content types
-
reuse schemas across content types (horizontal inheritance)
-
access and use the Headless API
If you want to build traditional web pages. Check out the guide: My first site. Don’t forget. You can also serve headless content from traditional Enonic sites as well ;-). |
Headless Movie Database
In this guide, we are using the demo application Headless Movie Data Base aka HMDB as a template for our project. The app may also be installed to your XP installation directly from Enonic Market.
Create project
Don’t have Enonic CLI? Visit the Getting started guide to install it before you continue. |
Create your project by running the following command:
enonic project create -r app-hmdb
Project structure
In the selected folder, you should now have a project structure, looking something like this:
src/
main/
resources/
import (1)
site/ (2)
content-types/ (3)
x-data/ (4)
sample/ (5)
1 | Sample content for Headless Movie Database |
2 | This is where CMS-specific functionality is placed |
3 | Content Types are placed in this directory |
4 | X-data enables editors to add additional fields to content across different content types |
5 | Sample files that will be used in the tutorial |
Building and Deploying
To build and deploy the app, simply run this command:
enonic project deploy
To verify that your app started successfully, you should find an entry similar to this in the sandbox log: 2019-04-09 13:40:40,765 INFO ... Application [com.example.myproject] installed successfully |
For the purposes of this guide, we’ll build and deploy our project manually for each step. If you want to redeploy changes automatically, you can either run in development mode (which will instantly pickup changes to your files), or use continuous build mode, which will compile and re-deploy the full application. Use the following commands respectively: enonic sandbox start --dev enonic project gradle deploy --continuous |
Content Studio
From the admin console (available on http://localhost:8080).
Launch Content Studio
from the XP menu.
Missing Content Studio? Open the Applications app, choose Install and find it in the list of apps that appear. |
Headless demo project
Your newly created app includes a sample dataset. When the application started, it automatically created a project called Headless Demo
and imported the HMDB
dataset into it.
Simply select this project when prompted, and you will see the demo content, similar to the screenshot below:

HMDB is built from three different content types: Person , Playlist and Movie . Each content type defines a specific form to edit and publish new items. |
Person Content Type
Let’s have a closer look at the content type "Person". When creating or editing a Person content, this is what the form looks like in the Content Studio App:

The form definition comes from the project file /src/main/resources/site/content-type/person/person.xml
.
eXtra Data
This form also has an additional step for "Social Media" as can be seen below:

This step is loaded from a so-called eXtra data file (x-data for short). The file is located in /src/main/resources/site/x-data/SoMe/SoMe.xml
The benefit of x-data is that it can be re-used across different content types (even across different apps within the same site).
Adding a custom content type
To make things even more interesting, we can extend the project with a new content type for reviews.
1 |
Move the file |
2 |
Optionally add icon by moving the file |
3 |
Build and deploy your application once more. |
4 |
Write A review. You should now be able to create a "Review" within Content Studio and the HMDB site. ![]() |
Accessing the API
To access and browse the API, simply point your browser to: http://localhost:8080/site/hmdb/draft/hmdb/api
The API browser will now load, with the documentation immediately available on the right hand side.

The API gives you read-access to all your content. Queries can now be typed into the left hand panel and executed, with the result in the right-hand panel.
New to GraphQL? After completing the tutorial, check out the GraphQL documentation. |
Below are a few examples of queries you can use to access the HMDB
data.
Sample queries
Persons
persons/
folder:
{
guillotine {
getChildren(key:"${site}/persons" first:5){
displayName
_path
}
}
}
${site} is a placeholder for the path of the contextual site. In this case it expands to /hmdb |
{
"data": {
"guillotine": {
"getChildren": [
{
"displayName": "Brad Pitt",
"_path": "/hmdb/persons/brad-pitt"
},
{
"displayName": "Keanu Reeves ",
"_path": "/hmdb/persons/keanu-reeves"
},
{
"displayName": "Carrie-Anne Moss ",
"_path": "/hmdb/persons/carrie-anne-moss"
},
{
"displayName": "The Wachowskis",
"_path": "/hmdb/persons/the-wachowskis"
},
{
"displayName": "Bruce Willis",
"_path": "/hmdb/persons/bruce-willis"
}
]
}
}
}
Movies and cast
Remember to replace com.example.myproject and com_example_myproject to match the name of your specific application (if you changed the project name during the project init). |
{
guillotine {
query(query: "type='com.example.myproject:movie'", first: 2) {
displayName
... on com_example_myproject_Movie {
data {
cast {
actor {
displayName
}
character
}
}
}
}
}
}
{
"data": {
"guillotine": {
"query": [
{
"displayName": "The Godfather",
"data": {
"cast": [
{
"actor": {
"displayName": "Al Pacino"
},
"character": " Michael Corleone"
}
]
}
},
{
"displayName": "The Shawshank Redemption",
"data": {
"cast": [
{
"actor": {
"displayName": "Tim Robbins"
},
"character": "Andy Dufresne"
},
{
"actor": {
"displayName": "Morgan Freeman"
},
"character": "Ellis Boyd 'Red' Redding"
},
{
"actor": {
"displayName": "Bob Gunton"
},
"character": "Warden Norton"
}
]
}
}
]
}
}
}
Person with photo
Remember to replace com.example.myproject and com_example_myproject to match the name of your application |
{
guillotine {
query(query: "ngram('_allText', 'morgan') AND type='com.example.myproject:person'", first: 6) {
displayName
... on com_example_myproject_Person {
displayName
data {
photos(first:1){
... on media_Image {
imageUrl(type:absolute scale:"block(400,400)")
}
}
}
}
}
}
}
{
"data": {
"guillotine": {
"query": [
{
"displayName": "Morgan Freeman",
"data": {
"photos": [
{
"imageUrl": "http://localhost:8080/site/default/draft/hmdb/_/image/7ab1f76a-69a1-490f-b505-6eb6773c7cec:603726cc4fa712aa1b70c7eb64e1349f664494c3/block-400-400/morgan-freeman.jpg"
}
]
}
}
]
}
}
}
When deployed to production, all URLs will be aligned with the production domain.
Creating a site from scratch
So far, we have used automatically imported content. To create your own site and content, simply use Content Studio once more.
To create your own site:
1 |
Click New at a desired location in the structure, and choose the content type "Site". |
2 |
Add your application to the site ![]() |
3 |
Start creating content within the new site |
You are completely free to group your content in folders and tree-structures, as well as setting permissions as you desire within the site, just like we do with HMDB . |
API setup
The /api
endpoint is exposed through a controller mapping in the file src/main/resources/site/site.xml
. The controller uses underlying functionality provided through a library. A dependency in the build.gradle
file, located at your project root ensures that the library is downloaded and built into your app.
Customizing API
The Headless API is provided by the Guillotine project. You may programmatically customize or extend the GraphQL api, as described in the documentation above.
CORS headers
Sharing of resources across domains (origins) is known as CORS. For security reasons, browsers will prevent POST requests from one domain to another by default.
GraphQL requests are normally POST requests, but our API is configured to allow traffic from any origin through the use of CORS headers.
If you want to limit access to a specific domains, or not at all, you may do so by customizing the controller file src/main/resources/controllers/graphql.js
.
Deploying to Production
To deploy your application to production, your first need a server running Enonic XP.
If you don’t already have a server, we recommend trying out Enonic XP on Google Cloud, or request an instance from the Enonic Cloud. If you are looking for other hosting options, Enonic XP is open source, and capable of running in any cloud or on premises. |
With a running server, you may deploy your app by uploading it via the Applications
admin tool.
After building/deploying an application locally, you will find the application file in your project’s build/libs/
folder. The file is typically called something like: myproject-1.0.0-SNAPSHOT.jar
.
Creating a headless project without HMDB
The HMDB application is based on the Headless Starter.
To create a clean project directly from the headless starter, simply run the following command:
enonic project create -r starter-headless
Bon voyage!