GraphQL API

Magnolia GraphQL is available as a public beta. You can install it with Maven or by placing pre-built JAR files in your webapp. The following JARs are required:

We do not anticipate the API changing significantly, so you can start trying it now. Please help us make this feature the best it can be. Report any bugs directly on the MGNLGQL Jira project, or send us bugs, questions, concerns and suggestions via this web form. In particular, if accessing content from the Pages app is an important use case for you, please review this page and give us your input.

This page describes the GraphQL endpoint for obtaining JCR data as JSON. GraphQL is most convenient if you need to retrieve content from a content app (a specialized app type for managing structured content, see Content Types) and you want control of exactly which properties and related content is included.

The example GraphQL requests and responses shown below on this page make use of the Book and Author catalog apps available in our demo bookcase light module. You can clone the module with this command:

git clone https://git.magnolia-cms.com/scm/documentation/bookcase.git

In the _dev/postmanCollections subfolder, the demo includes a Postman collection which you can use to quickly try all features.

Scope

Content types

Magnolia GraphQL API can only fetch content that has been declared using Magnolia content types. A GraphQL schema type is automatically created for every registered content type.

To check what GraphQL types are available, see the graphqlTypes in the Definitions app or send an introspection request to the endpoint. The following GraphQL schema types are available by default:

  • asset

  • date

  • jcrMetadata

    Best practice

    When creating names for content types, use singular and countable forms. Since Magnolia also generates plural variants of types for GraphQL schemas, double plural forms such as photographses or newses may appear awkward.

Query types

The API is read-only. The GraphQL query type is supported. The mutation type has not been implemented yet.

Content from Pages and Stories apps

The API cannot retrieve content used by the Pages app and the Stories app.

Sorting and metadata for assets

Currently, the sorting functionality cannot be applied to the results obtained from assets. Retrieving metadata properties is not yet supported.

Configuration aspects

Endpoint name and access control

By default, all GraphQL requests are mapped to /.graphql and pass through a filter chain. On a production instance, make sure that you have configured appropriate Access Control Lists (ACLs) and Web Access permissions for GraphQL. For more details, see GraphQL module: Configuration.

HTTP methods

The endpoint supports the POST and GET HTTP methods.

When using the POST method, you also need to set the Content-Type header in your request. The header type needs to reflect the format of content you intend to send in the request body.

POST method

Body format for a POST request with the application/graphql content type header:

{
    books(locale: "en") {
        title
    }
}

Body format for a POST request with the application/json content type header:

{
  "query":"{ books ( locale: \"en\" ) { title }}"
}

GET method

http://localhost:8080/.graphql?query=%7Bbooks%28locale%3A%20%22en%22%29%20%7Btitle%7D%7D

The value of the query parameter is a URL-encoded string {books(locale: "en") {title}}.

For more details how to create a GraphQL-valid header and body, see the HTTP Methods, Headers, and Body. All the examples below are based on the POST method.

Introspection

You can use the GraphQL schema introspection to inspect what GraphQL types and properties can be queried. See the following example introspection request body:

{
  __schema {
    types {
      name
    }
  }
}
Response
{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Asset"
        },
        {
          "name": "AssetRendition"
        },
        {
          "name": "Author"
        },
        {
          "name": "AuthorSort"
        },
        {
          "name": "Book"
        },
        {
          "name": "BookSort"
        },
        {
          "name": "Boolean"
        },
        {
          "name": "Date"
        },
        {
          "name": "ID"
        },
        {
          "name": "JcrMetadata"
        },
        {
          "name": "Locale"
        },
        {
          "name": "Long"
        },
        {
          "name": "Query"
        },
        {
          "name": "String"
        },
        {
          "name": "__Directive"
        },
        {
          "name": "__DirectiveLocation"
        },
        {
          "name": "__EnumValue"
        },
        {
          "name": "__Field"
        },
        {
          "name": "__InputValue"
        },
        {
          "name": "__Schema"
        },
        {
          "name": "__Type"
        },
        {
          "name": "__TypeKind"
        }
      ]
    }
  }
}

Querying content

You can query content of an individual item by providing either its path or its UUID to the path and id arguments, respectively.

Content from custom fields

Custom fields need an additional implementation to be exposed by the GraphQL endpoint. A custom field must be bound to a property in a content type.

Data fetching is consistent across all content types:

  • Requesting assets(path: "</some/path>") or contentTypes(path: "</some/path>") returns items directly under the path provided.

  • Requesting assets(path: "/") or contentTypes(path: "/") returns items directly under the root.

  • Requesting just assets or contentTypes returns all items regardless of folder structure.

Content references are resolved automatically. In the following examples, references are resolved for the authors and frontcover fields.

Single item by path

{
    book(path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species") {
        title
        authors {
            name
        }
        publisher
        publish_date
        frontcover {
            name
            link
        }
    }
}
Response
{
  "data": {
    "book": {
      "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life",
      "authors": [
        {
          "name": "Charles Darwin"
        }
      ],
      "publisher": "John Murray III",
      "publish_date": "1859-11-24",
      "frontcover": {
        "name": "darwin1859.png",
        "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png"
      }
    }
  }
}

Single item by UUID

{
    book(id: "c2a5ea72-6611-4f70-8005-42e8694fc610") {
        title
        authors {
            name
        }
        publisher
        publish_date
        frontcover {
            name
            link
        }
    }
}
Response
{
  "data": {
    "book": {
      "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life",
      "authors": [
        {
          "name": "Charles Darwin"
        }
      ],
      "publisher": "John Murray III",
      "publish_date": "1859-11-24",
      "frontcover": {
        "name": "darwin1859.png",
        "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png"
      }
    }
  }
}

All items

{
    books {
        title
    }
}
Response
{
  "data": {
    "books": [
      {
        "title": "The War of the Worlds"
      },
      {
        "title": "Contact"
      },
      {
        "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life"
      },
      {
        "title": "A Brief History of Time: From the Big Bang to Black Holes"
      },
      {
        "title": "God Created the Integers: The Mathematical Breakthroughs That Changed History"
      },
      {
        "title": "Brave New World"
      }
    ]
  }
}

Specific language variant

To request a specific language variant, use the locale argument. Locale-based content retrieval can be applied to both a single item and multiple items.

{
    book(
        path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species"
        locale: "de"
    ) {
        title
    }
}
Response
{
  "data": {
    "book": {
      "title": "Über die Entstehung der Arten durch natürliche Zuchtwahl oder die Erhaltung der begünstigten Rassen im Kampfe um’s Dasein"
    }
  }
}

With aliases

GraphQL aliases let you rename the result of a field to anything you want. This functionality is supported by the graphql-java library and works out of the box in the Magnolia GraphQL endpoint.

In the following example, the originalBookTitle prefix in the body specifies that the title field should be returned as originalBookTitle instead:

{
    book(
        path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species"
    ) {
        originalBookTitle: title
    }
}
Response
{
  "data": {
    "book": {
      "originalBookTitle": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life"
    }
  }
}

Retrieving assets

To fetch assets, you can query either the asset or the assets field. To retrieve a single asset, use the asset field and supply the UUID or path of the asset. To retrieve multiple assets, use the assets field and provide a path or UUID of the folder containing the assets.

jcr is the default value for the providerId argument. Unless you need to use a different asset provider, providerId can be omitted, as shown in the second example.

By UUID

{
    asset(providerId: "jcr", id: "da35e99f-6138-40c9-b7c1-57978aa70268") {
        name
        link
        path
    }
}
Response
{
  "data": {
    "asset": {
      "name": "darwin1859.png",
      "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png",
      "path": "/Book-Covers/darwin1859.png"
    }
  }
}

By path

{
    assets(path: "/Book-Covers") {
        name
        link
    }
}
Response
{
  "data": {
    "assets": [
      {
        "name": "wells1898.png",
        "link": "/dam/jcr:d6007e99-c6b1-44f4-ac6e-13c2eefa4379/wells1898.png"
      },
      {
        "name": "huxley1932.png",
        "link": "/dam/jcr:f8cfffe0-5383-4f89-ae5d-26e760dd68b8/huxley1932.png"
      },
      {
        "name": "sagan1997.png",
        "link": "/dam/jcr:a1a2ff1f-4580-4950-8576-0ec12ee35370/sagan1997.png"
      },
      {
        "name": "hawking2005.png",
        "link": "/dam/jcr:d5161c3b-2517-4476-9c18-c1c53886d555/hawking2005.png"
      },
      {
        "name": "hawking1988.png",
        "link": "/dam/jcr:f964ee41-d533-4a21-bb61-8c576498b868/hawking1988.png"
      },
      {
        "name": "darwin1859.png",
        "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png"
      }
    ]
  }
}

Resolving asset renditions

To resolve asset renditions, renditions must be defined in a theme that is linked to a site definition.

Let’s assume there is a theme definition called book-theme in the bookcase light module:

<light-modules-folder>/bookcase/themes/book-theme.yaml

imaging:
  class: info.magnolia.templating.imaging.VariationAwareImagingSupport
  variations:
    "1600":
      class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
      width: 1600
    "1366":
      class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
      width: 1366
    "240":
      class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
      width: 240

The definition is linked to a site associated with the domain you query with GraphQL requests:

<site-name>:
  theme:
    name: book-theme

Then you can issue the following requests.

All renditions

{
    book(path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species") {
        title
        frontcover {
            name
            link
            renditions {
                renditionName
                link
            }
        }
    }
}
Response
{
  "data": {
    "book": {
      "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life",
      "frontcover": {
        "name": "darwin1859.png",
        "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png",
        "renditions": [
          {
            "renditionName": "1600",
            "link": "/.imaging/mte/book-theme/1600/dam/Book-Covers/darwin1859.png/jcr:content/darwin1859.png"
          },
          {
            "renditionName": "1366",
            "link": "/.imaging/mte/book-theme/1366/dam/Book-Covers/darwin1859.png/jcr:content/darwin1859.png"
          },
          {
            "renditionName": "240",
            "link": "/.imaging/mte/book-theme/240/dam/Book-Covers/darwin1859.png/jcr:content/darwin1859.png"
          }
        ]
      }
    }
  }
}

One rendition

{
    book(path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species") {
        title
        frontcover {
            name
            link
            renditions(renditionNames: "1600") {
                renditionName
                link
            }
        }
    }
}
Response
{
  "data": {
    "book": {
      "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life",
      "frontcover": {
        "name": "darwin1859.png",
        "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png",
        "renditions": [
          {
            "renditionName": "1600",
            "link": "/.imaging/mte/book-theme/1600/dam/Book-Covers/darwin1859.png/jcr:content/darwin1859.png"
          }
        ]
      }
    }
  }
}

More renditions

{
    book(path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species") {
        title
        frontcover {
            name
            link
            renditions(renditionNames: ["1600","240"]) {
                renditionName
                link
            }
        }
    }
}
Response
{
  "data": {
    "book": {
      "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life",
      "frontcover": {
        "name": "darwin1859.png",
        "link": "/dam/jcr:da35e99f-6138-40c9-b7c1-57978aa70268/darwin1859.png",
        "renditions": [
          {
            "renditionName": "1600",
            "link": "/.imaging/mte/book-theme/1600/dam/Book-Covers/darwin1859.png/jcr:content/darwin1859.png"
          },
          {
            "renditionName": "240",
            "link": "/.imaging/mte/book-theme/240/dam/Book-Covers/darwin1859.png/jcr:content/darwin1859.png"
          }
        ]
      }
    }
  }
}

Retrieving content metadata

Values of the following metadata properties can be retrieved for each content item via GraphQL:

  • id

  • name

  • path

  • nodeType

Example:

{
    book(path: "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species") {
        title
        _metadata {
            id
            name
            path
            nodeType
        }
    }
}
Response
{
  "data": {
    "book": {
      "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life",
      "_metadata": {
        "id": "c2a5ea72-6611-4f70-8005-42e8694fc610",
        "name": "Darwin-1859-On-the-Origin-of-Species",
        "path": "/My-Good-Reads/Science/Darwin-1859-On-the-Origin-of-Species",
        "nodeType": "lib:book"
      }
    }
  }
}

Retrieving other metadata properties

You can use the definition decoration mechanism to define additional metadata properties that should also be retrievable through GraphQL.

In the following example, an additional created field is defined to allow accessing the mgnl:created property:

fields:
  - name: 'created'
    type:
      name: Date
      nonNull: true
    dataFetcher:
      class: info.magnolia.graphql.jcr.datafetcher.JcrPropertyDataFetcherDefinition
      propertyName: 'mgnl:created'

Paging

Paging of results in Magnolia GraphQL can be achieved using the limit and offset arguments. Their default values are 20 and 0, respectively.

In the following body definition, only the first four entries for the title field are requested:

{
    books(limit: 4) {
        title
    }
}
Response
{
  "data": {
    "books": [
      {
        "title": "The War of the Worlds"
      },
      {
        "title": "Contact"
      },
      {
        "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life"
      },
      {
        "title": "A Brief History of Time: From the Big Bang to Black Holes"
      }
    ]
  }
}

With limit and offset set to 2 and 1, respectively, titles two and three are delivered:

{
    books(limit: 2, offset: 1) {
        title
    }
}
Response
{
  "data": {
    "books": [
      {
        "title": "Contact"
      },
      {
        "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life"
      }
    ]
  }
}

Sorting

For scalar JCR-based GraphQL types, Magnolia automatically generates custom ENUMs in the form <FIELDNAME>_ASC and <FIELDNAME>_DESC. These ENUMs then describe the fields that can be sorted. Field names must be given in the upper case form.

{
    books(sort: TITLE_DESC) {
        title
    }
}
Response
{
  "data": {
    "books": [
      {
        "title": "The War of the Worlds"
      },
      {
        "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life"
      },
      {
        "title": "God Created the Integers: The Mathematical Breakthroughs That Changed History"
      },
      {
        "title": "Contact"
      },
      {
        "title": "Brave New World"
      },
      {
        "title": "A Brief History of Time: From the Big Bang to Black Holes"
      }
    ]
  }
}

You can also chain the sort operations, for example sort: [AUTHOR_ASC, TITLE_ASC] first sorts the results by the author property and then by the title property.

Filtering

To filter the results, you can use a number of simple filters and operators that can be chained using the AND and OR conjunctions into a multiple predicate. The conjunctions can be used in either case, lower or upper. In case of JCR, AND has precedence before OR. Parentheses,( and ), can be used for grouping the filters.

Example filter requesting title fields that contain War or Struggle:

{
    books(filter: "@title LIKE '%War%' OR @title LIKE '%Struggle%'") {
        title
    }
}
Response
{
  "data": {
    "books": [
      {
        "title": "The War of the Worlds"
      },
      {
        "title": "On the Origin of Species by Means of Natural Selection, or the Preservation of Favoured Races in the Struggle for Life"
      }
    ]
  }
}

Filters

Numbers

Number matching is done against the type stored in the repository.

  • Integers (longs in case of JCR):

    • filter: "@bar = 123"

  • For floats (doubles in case of JCR), use the floating-point format or the f suffix:

    • filter: "@bar = 123.0"

    • filter: "@bar = 123f"

    • filter: "@bar = 123.0f"

  • For big decimals (decimals in case of JCR), use the D suffix:

    • filter: "@bar = 123D"

    • filter: "@bar = 123.0D"

Booleans

For booleans, use either true or false. Both letter cases are allowed:

  • filter: "@foo = true"

  • filter: "@foo = FALSE"

Nulls

Nulls can also be declared in both letter cases. For JCR-stored data, these are property existence checks, not empty value checks.

  • filter: "@foo = null"

  • filter: "@foo <> NULL"

Strings

Strings must be enclosed in single quotes:

  • filter: "@foo = 'Some String'"

To search within a string, use the percentage sign with the LIKE operator:

  • filter: "@foo LIKE '%World%'"

Dates

For filtering by dates, the JCR implementation recognizes the ISO 8601 date and time format:

  • filter: "@fooDate = '2019-02-11T00:00:00+02:00'"

Property names

To filter property names, a property must start with the at sign (@) and match the [_A-Za-z][_0-9A-Za-z]* regex pattern, for example @foo. Properties can be referenced using the dot notation, such as @stats.speed in:

  • filter: "@stats.speed = 200"

In case of JCR, this will create a condition that tries to filter against a submodel named stats and its speed property.

Operators

Form Meaning

=

equal

<>

not equal

>

greater than

<

lesser than

lesser than or equal

>=

greater than or equal

LIKE

Can be used with:

  • The percentage sign (%), a wildcard character that stands for zero or more additional characters.

like

Further resources

Feedback