This page explains how to access Magnolia content from a client-side application. We use Magnolia's built-in REST API and a single-page AngularJS application.
We use the classic cars from the My first content app tutorial as sample content. The cars are typical structured content: each car has the same content type and same fields. Structured content is easy to enter, query, analyze and publish to multiple channels. You can publish it to a website or consume it from a client-side application like we do in this tutorial. With the REST API, you can access any Magnolia workspace.
- Install the Magnolia REST module
- Think about the REST security
- Test REST access
- Write an Angular app
Install Magnolia REST
Make sure that your Magnolia installation contains the REST module. You should have at least the following submodules:
If you use a preconfigured Magnolia bundle or webapp you already have the required modules. If you use a custom bundle check your project dependencies and add Magnolia REST module if not already there.
If you want to use Swagger to test the REST endpoints, also install the
magnolia-rest-tools module. It is not required to run the Angular app but it can be helpful during development.
magnolia-rest-tools, set the
apiBasepath. The default value is most likely not correct for your Magnolia instance.
The Magnolia REST module comes with preconfigured REST endpoints to read data from content apps. In this tutorial, we access content stored in the JCR so we use the following endpoints:
These two endpoints allow you to create, update and delete nodes and properties in any JCR workspace of your Magnolia instance.
Tip: You can also implement custom REST endpoints. You don't need them in this tutorial but custom endpoints are useful for:
- Getting a very specific data structure which is different from what you get when using the preconfigured endpoints.
- Exposing data from a custom content app which does not store the data in the JCR.
- Getting data from more than one workspace within one request.
The preconfigured REST endpoints provide not only methods to read but create, edit and update data. Configure a specific role which meets your requirements. Add the role to the user group which should have access to the required REST methods. For instance, you could configure a role which can only read the nodes and properties of a specific workspace, then add this role to the
anonymous user. During development it will help to add this new role to
Install sample content
In this tutorial we use the Products app and the classic cars as sample content. The cars are stored in the
products JCR workspace.
Maven is the easiest way to install the module. Add the following to your bundle:
<dependency> <groupId>info.magnolia.documentation</groupId> <artifactId>magnolia-app-tutorial</artifactId> <version>1.4.7</version> </dependency>
Pre-built jars are also available for download. See Installing a module for help.
If you don't have a Magnolia instance yet, follow Installing Magnolia and a content app, then come back here. You will end up with exactly what we want to have for this tutorial as well.
Grant permissions to access content via REST
Grant permission to read content from the workspace
products to a new role.
- Open the Security app.
- Add a role
- In Access control lists, grant the new role:
- Read-only access to
/and its subnodes in the
- Read-only access to
- Read-only access to
- In Web access, grant
Getaccess to the path
/.rest/nodes/v1/products*. Deny all others.
- On the author instance, add the
read-productsrole to the
- On the author instance, publish the
- On the public instance, add the
read-productsrole to the
- Log out and log back in to apply the new permissions to the currently logged-in user. This gets you a new session.
Test the app
Open the Products app installed by the
app-tutorial module and get familiar with it.
Since the items in the app are cars let's talk about cars from now on. Try the tree, list and thumbnail views. Add a car of your own.
Test REST access
Request the app content with a REST call. Try the following request in your browser:
Magnolia responds with:
Open a terminal and type the following command:
Now we get JSON:
If you installed the
magnolia-rest-tools module, request the same with Swagger:
- Open the REST Tools app.
- Click GET in the
- Set workspace to
productsand path to a car such as
/cars/007/Aston-Martin-DB5and click Try it out. You get JSON data for one content item.
Play around a little bit to familiarize yourself with the Swagger tool. Figure out the correct parameters to get a JSON representation of all cars.
Create an AngularJS app
The content items we render are cars. They are organized in the app like this:
- the cars are of type
Let's create an Angular app that provides:
- List of cars to choose from
- Details of a selected car with description and image
- Checkboxes to filter the list into
007cars. We use the parent folders as pseudo-categories.
carsListrenders a list of all cars. Each car has a clickable label. Clicking on the label shows the car in the
carDetailcontroller. The list controller also has two checkboxes to filter the list - one for each parent folder.
carDetailrenders the detail of the selected car: title, description and image.
Create the following file structure:
$lightmodules is your light module directory. It can be anywhere on your file system but it must be a real directory such as:
- Mac OS X
You may want to set the
magnolia.resources.dir property to reference your light modules folder:
Start creating the Angular app in
erics-cars.html. Add an
ng-app directive to the
body element which will wrap the two controllers:
- Lines 5-7: References to JS and CSS files. We use dummy request parameters such as
?u=12to bypass cache. We recommend that you use such dummy request parameters while developing. Alternatively, you could exclude content from the cache on the Magnolia instance but your browser may also cache the resources, particularly JS and CSS files. Once you're done with development, remove the request parameters.
- Line 10:
ng-appdirective in the
Request the file in the browser to make sure that Magnolia serves a static file. Use a dummy parameter
?foo=bar also in the request URL. It ensures we bypass cache on both server and client side.
Next, configure the Angular app in
CSS style sheet
To style the Eric's car page, add some CSS code to
carDetail controller and AngularJS utilities
To render a car's details we want a
$scope object of the
carDetail controller with the following properties:
Add the following HTML snippet to
Add a component named
globalData. Both controllers will use it. It has a property
productPath which will be set by one controller and read by the other controller.
Add a component named
utils. It provides some useful methods that both controllers can use. The
carDetail controller will use the function
getPropertyValue(node, propertyName)is a convenience method to fetch a property from the JSON structure is returned by the standard nodes endpoint.
replaceAll(str, find, replace)manipulates strings. The
carDetailcontroller uses this function just to style things a bit.
The third little helper to add is the
sanitize filter. Remember that the
description property of the content item contains HTML, encoded HTML even. By default, Angular refuses to render JSON content that contains (HTML) markup. The
sanitize filter makes sure that the (encoded) HTML is rendered properly as HTML.
Finally, add the
- Line 1: Custom components
- Line 9: The controller executes an HTTP#get method on the
nodesREST endpoint. The content item path is the selected item. It requests data for one content item as we did in testing REST access.
- Line 8, 9: Computes a request URL for the REST call using configuration data from the
- Line 3ff: The HTTP#get call is defined as a callback function of
$watchwhich is a value change listener for
globalData.productPath. The REST call is executed initially and when
Reload the file in your browser again. Now you can see the detail of the Aston Martin DB5 in your browser. Cool! Isn't it?
carsList controller renders a list of clickable
span elements that contain car titles. The list can be filtered with checkboxes using the parent folders
007 as pseudo-categories.
Add this HTML in
cars: An array of car items. Each car item has these properties:
category: actually a pseudo-category, the name of the parent folder such as
title: Title of the car
name: Node name of the item.
carCategories: An associative array (map) for the pseudo-categories. Each item has one property:
checked: Whether the pseudo-category is currently selected.
- Line 1: Again custom components
- Lines 5-7:
selectCaris executed when you click a car of the list. The function sets
globalData.productPathso it indirectly triggers the execution of the
- Lines 9-11:
clickCategorymaintains the $scope variable
carCategories. Since this changes the state of a controller $scope variable, the UI gets repainted - the list items gets updated.
- Line 3: The controller executes an HTTP#get method. Here the URI for the JSON call requests the cars root folder and contains the argument
Get the completed files
The three files in their final form are available in Magnolia's Git repository.
Option 1: Clone the Git repository
Clone the repository to get the complete file structure. Use a terminal, go to your light modules folder, and clone:
Option 2: Download
Custom JSON format
The JSON format provided by the
utils component of the Angular app. But at some point you may want a JSON representation that you cannot get easily with the default JCR
With a custom JSON service you can also get data from different JCR workspaces in one request. This cannot be done with the default endpoints for nodes and properties. A custom service therefore saves resources on the network.
With Java: create a custom endpoint
Another solution is the
neat-jsonfn module. Read the blog post and check out the module in GitHub:
- JSON, REST, Magnolia and the creation of endpoints without writing a single line of Java (blog post)
- neat-jsonfn (GitHub repository)
Combine your content app with the Categorization module
The Products app used in this tutorial is very simple. We use folders to mimic car categories. Better use the Categorization module to add multiple categories.
Where to add the AngularJS app?
You can use or host the AngularJS app anywhere:
- As static web resource served by Magnolia as seen in this tutorial
- As part of a page or component template, in a template script
- As a static or dynamic resource in any other server
If you want to run the AngularJS app on a distinct host – a host which has a different address than the Magnolia server which provides the REST API – you run into the same-origin policy problem. To overcome this problem some use specific Apache settings, see StackOverflow.
An elegant solution to solve the same-origin policy without changing Apache configuration is the Magnolia CORSFilter:
- Riley Brooklands 1930 and Fiat Cinquecento, Pedro Ribeiro Simões. Creative Commons Attribution 2.0 Generic (CC BY 2.0)
- Pontiac 1952, Continental Mark II and Hudson 1927, John Lloyd. Creative Commons Attribution 2.0 Generic (CC BY 2.0)
Aston Martin DB5, Aston Martin Works
- Lotus Esprit S1, Don Griffin