Introduction to Enonic
Contents
Hands on intro to our platform, CLI, Content Studio, headless API, and free hosting tier.
Just looking for a demo? - Sign up for a free Enonic account. Create a new solution based on the Movie DB template to get a live demo using the same setup as this tutorial. |
Introduction
This guide will show you how to:
-
set up a local Enonic developer environment
-
create an Enonic app
-
install and use Content Studio
-
customize the app by adding a new content type
-
access and use the GraphQL API
-
optionally - deploy your app to a live server
Install CLI
The Enonic Command Line Interface is an essential tool for developers working with Enonic.
If you have npm
on your device, run this command:
npm install -g @enonic/cli
If not, here are some alternative ways to install Enonic CLI
To verify that the CLI has been installed, run enonic -v
. This should output the version of your installed CLI.
To see all available options, simply run enonic
.
To upgrade, use enonic latest . If there are new versions you will see instructions on how to upgrade. |
Create a Sandbox
A sandbox is a local developer instance of our platform - Enonic XP. Create a sandbox by running this command in your terminal:
enonic sandbox create
Give it a name, i.e. intro
, and select the most recent version of Enonic XP from the list that appears.
Start the sandbox with this command:
enonic sandbox start --dev
Select the intro
sandbox from the list, and it will boot up in development mode.
Dev mode will automatically load changes in your code when developing. |

Create an app
Enonic apps are used to ship everything from content models, to code and platform extensions.
From a new terminal window, create your first Enonic app.
Use the default options when prompted. |
enonic project create -r app-moviedb
Select the intro
sandbox from the list, and the app will use this from now on.
The command uses the Movie DB app as a starter (template) for your app. |
Project structure
The previous step created the folder myproject
, you should now have an app structure, looking something like this:
docs/ (1)
src/
main/
resources/
import (2)
site/
content-types/ (3)
x-data/ (4)
samples/ (5)
gradle.properties (6)
1 | The documentation you are reading now |
2 | Movie DB content |
3 | Content Types schemas are placed in this directory |
4 | X-data schemas are placed here |
5 | Code samples that will be used in this guide |
6 | App settings and name |
Deploy the app
Assuming you did not change the default directory name myproject/
when creating the app, run the following commands:
cd myproject enonic dev
This last command will make sure to build and deploy the app to your intro
sandbox, and sequentially keep watching for changes on server side code to automatically redeploy your app as you work on it.
Look for a line like this in the sandbox log to verify that the app has started: 2019-04-09 13:40:40,765 INFO ... Application [com.example.myproject] installed successfully |
Install Content Studio
Content Studio is the editorial interface used to create and manage content. It is not a part of the core platform, but as you will see soon, it can easily be installed from Enonic Market.
-
Open the sandbox admin: http://localhost:8080/admin and click
Login without a user
. This will bring you to the Dashboard. -
Open the Applications app from the top right
XP menu → Applications
. -
Install Content Studio: click
Install
button in the menu bar, scroll down toContent Studio
(or use search) in the list of apps that appears and clickInstall
next to it.
Browse content
When your application was installed, it automatically created a Content Studio project called Movie DB
, and imported sample content. Let’s have a look:
-
Open Content Studio, which is now available in
XP menu → Content Studio
.
Select the Movie DB
project when prompted, and you should now see the imported content.

Content type: Person
To edit an item, simply select it and click Edit
in the menu bar, or by using right click. When editing a Person content item, this is what the form looks like in Content Studio. For bigger screens, you will also get a preview on the right hand side.

The form definition comes from the content type definition, which is located within the app. Here is what it looks like.
<?xml version="1.0" encoding="UTF-8"?>
<content-type xmlns="urn:enonic:xp:model:1.0">
<display-name>Person</display-name>
<description>Historical or living human</description>
<super-type>base:structured</super-type>
<form>
<input type="ImageSelector" name="photos">
<label>Photos</label>
<occurrences minimum="0" maximum="0"/>
</input>
<input type="TextArea" name="bio">
<label>Bio</label>
<occurrences minimum="1" maximum="1"/>
</input>
<input type="Date" name="dateofbirth">
<label>Birth Date</label>
<occurrences minimum="0" maximum="1"/>
</input>
</form>
</content-type>
X-Data: Social media
When editing a Person, the form contains an additional set of fields called Social Media
. This is strictly speaking a separate form, injected into the Person content type - we call it an x-data.

The cool thing about x-data is not the name, but the fact that it can be applied across multiple content types. In this app for instance, the Social Media form is also available on the Movie
content. X-data can even be injected from a different application - at runtime.
This is what the definition looks like:
<?xml version="1.0" encoding="UTF-8"?>
<x-data>
<display-name>Social Media</display-name>
<form>
<input type="TextLine" name="imdb">
<help-text>URL to IMDB</help-text>
<label>Internet Movie Database Url</label>
<occurrences minimum="0" maximum="1"/>
</input>
<input type="TextLine" name="twitter">
<help-text>Twitter Handle - i.e. @enonicHQ</help-text>
<label>Twitter</label>
<occurrences minimum="0" maximum="1"/>
<config>
<regexp>^@?(\w){1,15}$</regexp>
</config>
</input>
<input type="TextLine" name="instagram">
<help-text>Instagram Handle - i.e. @username</help-text>
<label>Instagram</label>
<occurrences minimum="0" maximum="1"/>
<config>
<regexp>^@?(\w){1,28}$</regexp>
</config>
</input>
</form>
</x-data>
Adding a new content type
Follow the steps below to add a Review
content type to your app.
-
Copy or move the file
samples/review/review.xml
tosrc/main/resources/site/content-types/review/review.xml
. -
Add an icon by copying or moving the file
samples/review/review.svg
to the new content type folder as well. This will give your reviews a nice dice icon when seen in Content Studio. -
Write a review. Content Studio should pickup the changes immediately. Anywhere in the structure, click
New → Review
and start writing.
Graphql API
The headless API aka Guillotine - yes, we take headlessness seriously around here - is not part of the core platform. Like Content Studio, it can be installed from Enonic Market.
Install the Guillotine app from XP menu → Applications → Install
and find it in the list of apps that appear.
The Guillotine API provides read-only-access to all of the content within the contextual project. Users requesting content will naturally only get access to the content he/she has read-permissions for. The Movie DB project is setup with public read access. |
After installing Guillotine, Content Studio’s left hand menu will reveal a new menu item called Query Playground
. Here, you may test and play with the GraphQL API directly.

The API documentation is available from the top left "file cabinet" icon. Use the dropdown on the top right to query against the draft or master branch, accessing the draft and published items respectively.
Want to know more about GraphQL in general? Visit the official GraphQL documentation.
Sample queries
Below are some example queries you can use for the Movie DB content.
Get Persons
persons/
folder:
{
guillotine {
getChildren(key:"/persons" first:3){
displayName
_path
}
}
}
{
"data": {
"guillotine": {
"getChildren": [
{
"displayName": "Léa Seydoux",
"_path": "/persons/lea-seydoux"
},
{
"displayName": "Jeffrey Wright",
"_path": "/persons/jeffrey-wright"
},
{
"displayName": "Daniel Craig",
"_path": "/persons/daniel-craig"
}
]
}
}
}
Query variables
GraphQL also supports the concept of query variables. Similar to parameters in functions, you may pass variables to a query.
Query variables are defined with JSON. You can add them from the Variables
option below the query editor.
{
"path": "/persons"
}
With the variable set, update your query to use the variable. This query should provide the exact same response as it did before.
query($path:ID!){
guillotine {
getChildren(key:$path first:5){
displayName
_path
}
}
}
Movies and cast
If you changed the name of your app when creating it, you must replace com.example.myproject and com_example_myproject for the query below to work. |
{
guillotine {
query(query: "type='com.example.myproject:movie'", first: 1) {
displayName
... on com_example_myproject_Movie {
data {
cast {
actor {
displayName
}
character
}
}
}
}
}
}
{
"data": {
"guillotine": {
"query": [
{
"displayName": "The Matrix",
"data": {
"cast": [
{
"actor": {
"displayName": "Keanu Reeves"
},
"character": "Neo"
},
{
"actor": {
"displayName": "Carrie-Anne Moss"
},
"character": "Trinity"
}
]
}
}
]
}
}
}
Image handling
In this case, Guillotine plays tag-team with Enonic’s image service, which is capable of delivering real-time cropped and optimized versions of images. In this case producing a 400 x 400px version of the image.
The query below show the name of actors containing the term morgan
, and provide a link to a cropped image of the actor.
{
guillotine {
query(query: "ngram('_allText', 'morgan') AND type='com.example.myproject:person'", first: 6) {
... 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/admin/site/preview/moviedb/draft/_/image/7ab1f76a-69a1-490f-b505-6eb6773c7cec:603726cc4fa712aa1b70c7eb64e1349f664494c3/block-400-400/morgan-freeman.jpg"
}
]
}
}
]
}
}
}
When looking at the result in Query Playground, you can see the actual image by hovering over the link:

The original higher resolution image is stored as a content item, just like persons and reviews. This is what it actually looks like:

The red "autofocus" circle, when set, helps the image service to crop the images optimally - as you can see from above. |
API endpoint
So far, you’ve been playing with the API via Content Studio, if you want to access the API’s endpoint directly, its available at respectively
-
http://localhost:8080/site/moviedb/draft (draft items)
-
http://localhost:8080/site/moviedb/master (published items).
The API can be deployed to a custom domain (aka vhost) when you go live. Also, important to notice - the GraphQL API is accessed via HTTP POST method, as such - visiting via a regular browser will render a 404.
The Chrome extension Altair lets you browse any GraphQL API, simply by pointing it to the URL. |
Deploying to Production
To deploy your application to production, you’ll need Enonic XP running on a server.
If you are looking for other hosting options, Enonic XP is open source, and can be deployed pretty much anywhere. |
-
Start by signing up for a free account. You must complete the verification steps before you can move on.
-
Log in to the Enonic console , and Create a new solution from the menu
Solutions → CMS essentials template
. Complete the solution wizard steps using your preferences.Similar to a sandbox, this will spin up an instance of Enonic XP, but this time as real server in the cloud.
-
Once the solution has started, authenticate the CLI by running this command:
enonic cloud login
After successfully authenticating, install your app using this command:
You must execute the command from your app folder. enonic cloud app install
Alternatively upload the app manually via
Enonic console → <My solution> → Applications → Install
.The app file is in your project folder i.e.
build/libs/<myproject>.jar
. -
Expose your API by creating a Route:
Enonic console → <My solution> → Routes → Create
.Give it a suitable name i.e.
Movie DB API
, and use the following values:-
Internal path:
/site/moviedb/master
-
Public path:
/api
-
ID provider: leave empty
-
-
Tadaa! Your published content can now be queried directly on the URL created by this route.
Dive deeper
🎉 Sweet! You made it to the end.
In the process, you built and customized your own app, installed apps from Enonic Market, ran queries, got to know the Enonic CLI and our hosted offerings.
As you may imagine, the platform is capable of so much more. Did you for instance know Enonic has a JavaScript framework, will full TypeScript definitions. You may use it in your apps to extend and customize the platform as you like.
To continue the journey, we recommend visiting the Enonic tutorials section of the developer portal.
Bon voyage!