AEM GraphQL API for use with Content Fragments

The Adobe Experience Manager as a Cloud Service (AEM) GraphQL API used with Content Fragments is heavily based on the standard, open source GraphQL API.

Using the GraphQL API in AEM enables the efficient delivery of Content Fragments to JavaScript clients in Headless CMS implementations:

  • Avoiding iterative API requests as with REST,
  • Ensuring that delivery is limited to the specific requirements,
  • Allowing for bulk delivery of exactly what is needed for rendering as the response to a single API query.
NOTE

GraphQL is currently used in two (separate) scenarios in Adobe Experience Manager (AEM) as a Cloud Service:

The GraphQL API

GraphQL is:

  • …a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.”.

    See GraphQL.org

  • …an open spec for a flexible API layer. Put GraphQL over your existing backends to build products faster than ever before…”.

    See Explore GraphQL.

  • “…a data query language and specification developed internally by Facebook in 2012 before being publicly open sourced in 2015. It provides an alternative to REST-based architectures with the purpose of increasing developer productivity and minimizing amounts of data transferred. GraphQL is used in production by hundreds of organizations of all sizes…”

    See GraphQL Foundation.

For further information about the GraphQL API, see the following sections (amongst many other resources):

The GraphQL for AEM implementation is based on the standard GraphQL Java Library. See:

GraphQL Terminology

GraphQL uses the following:

See the (GraphQL.org) Introduction to GraphQL for comprehensive details, including the Best Practices.

GraphQL Query Types

With GraphQL you can perform queries to return either:

The GraphQL for AEM Endpoint

The endpoint is the path used to access GraphQL for AEM. Using this path you (or your app) can:

  • access the GraphQL schema,
  • send your GraphQL queries,
  • receive the responses (to your GraphQL queries).

The repository path of the GraphQL for AEM endpoint is:

/content/cq:graphql/global/endpoint

Your app can use the following path in the request URL:

/content/_cq_graphql/global/endpoint.json

To enable the endpoint for GraphQL for AEM you need to:

CAUTION

These steps are liable to change in the near future.

Enabling your GraphQL Endpoint

NOTE

See Supporting Packages for details of the packages that Adobe provides to help simplify these steps.

To enable GraphQL queries in AEM, create an endpoint at /content/cq:graphql/global/endpoint:

  • Nodes cq:graphql and global must be of type sling:Folder.
  • Node endpoint must be of type nt:unstructured and contain a sling:resourceType of graphql/sites/components/endpoint.
CAUTION

In certain releases there is currently a known issue with the endpoint:

  • Depending on the release used, the entry cq:graphql is seen in the Sites console; at the top level.
    This must not be used.
CAUTION

The endpoint is accessible to everyone. This can - especially on publish instances - pose a security concern, as GraphQL queries can impose a heavy load on the server.

You can set up ACLs, appropriate to your use case, on the endpoint.

NOTE

Your endpoint will not work out-of-the-box. You will have to provide Additional Configurations for GraphQL Endpoint separately.

NOTE

Additionally you can test and debug GraphQL queries using the GraphiQL IDE.

Additional Configurations for GraphQL Endpoint

NOTE

See Supporting Packages for details of the packages that Adobe provides to help simplify these steps.

Additional configurations are required:

  • Dispatcher:
    • To allow required URLs
    • Mandatory
  • Vanity URL:
    • To allocate a simplified URL for the endpoint
    • Optional
  • OSGi Configuration:
    • GraphQL Servlet Configuration:
      • Handles requests to the endpoint
      • The configuration name is org.apache.sling.graphql.core.GraphQLServlet. It needs to be provided as an OSGi factory configuration
      • sling.servlet.extensions must be set to [json]
      • sling.servlet.methods must be set to [GET,POST]
      • sling.servlet.resourceTypes must be set to [graphql/sites/components/endpoint]
      • Mandatory
    • Schema Servlet Configuration:
      • Creates the GraphQL schema
      • The configuration name is com.adobe.aem.graphql.sites.adapters.SlingSchemaServlet. It needs to be provided as an OSGi factory configuration
      • sling.servlet.extensions must be set to [GQLschema]
      • sling.servlet.methods must be set to [GET]
      • sling.servlet.resourceTypes must be set to [graphql/sites/components/endpoint]
      • Mandatory
    • CSRF Configuration:
      • Security protection for the endpoint
      • The configuration name is com.adobe.granite.csrf.impl.CSRFFilter
      • Add /content/cq:graphql/global/endpoint to the existing list of excluded paths (filter.excluded.paths)
      • Mandatory

Supporting packages

To simplify the setup of a GraphQL endpoint, Adobe provides the GraphQL Sample Project package.

This archive contains both the required additional configuration and the GraphQL endpoint. If installed on a plain AEM instance, it will expose a fully working GraphQL endpoint at /content/cq:graphql/global/endpoint.

This package is meant to be a blueprint for your own GraphQL projects. See the package README for details on how to use the package.

Should you prefer to manually create the required configuration, Adobe also provides a dedicated GraphQL Endpoint Content Package. This content package contains the GraphQL endpoint only, without any configuration.

GraphiQL Interface

An implementation of the standard GraphiQL interface is available for use with AEM GraphQL. This can be installed with AEM.

This interface allows you to directly input, and test, queries.

For example:

  • http://localhost:4502/content/graphiql.html

This provides features such as syntax-highlighting, auto-complete, auto-suggest, together with a history and online documentation:

GraphiQL Interface

Installing the AEM GraphiQL interface

The GraphiQL user interface can be installed on AEM with a dedicated package: the GraphiQL Content Package v0.0.4 package.

Use Cases for Author and Publish Environments

The use cases can depend on the type of AEM as a Cloud Service environment:

  • Publish environment; used to:

    • Query data for JS application (standard use-case)
  • Author environment; used to:

    • Query data for “content management purposes”:
      • GraphQL in AEM as a Cloud Service is currently a read-only API.
      • The REST API can be used for CR(u)D operations.

Permissions

The permissions are those required for accessing Assets.

Schema Generation

GraphQL is a strongly typed API, which means that data must be clearly structured and organized by type.

The GraphQL specification provides a series of guidelines on how to create a robust API for interrogating data on a certain instance. To do this, a client needs to fetch the Schema, which contains all the types necessary for a query.

For Content Fragments, the GraphQL schemas (structure and types) are based on Enabled Content Fragment Models and their data types.

CAUTION

All the GraphQL schemas (derived from Content Fragment Models that have been Enabled) are readable through the GraphQL endpoint.

This means that you need to ensure that no sensitive data is available, as it could be leaked this way; for example, this includes information that could be present as field names in the model definition.

For example, if a user created a Content Fragment Model called Article, then AEM generates the object article that is of a type ArticleModel. The fields within this type correspond to the fields and data types defined in the model.

  1. A Content Fragment Model:

    Content Fragment Model for use with GraphQL

  2. The corresponding GraphQL schema (output from GraphiQL automatic documentation):
    GraphQL Schema based on Content Fragment Model

    This shows that the generated type ArticleModel contains several fields.

    • Three of them have been controlled by the user: author, main and referencearticle.

    • The other fields were added automatically by AEM, and represent helpful methods to provide information about a certain Content Fragment; in this example, _path, _metadata, _variations. These helper fields are marked with a preceding _ to distinguish between what has been defined by the user and what has been auto-generated.

  3. After a user creates a Content Fragment based on the Article model, it can then be interrogated through GraphQL. For examples, see the Sample Queries (based on a sample Content Fragment structure for use with GraphQL).

In GraphQL for AEM, the schema is flexible. This means that it is auto-generated each and every time a Content Fragment Model is created, updated or deleted. The data schema caches are also refreshed when you update a Content Fragment Model.

The Sites GraphQL service listens (in the background) for any modifications made to a Content Fragment Model. When updates are detected, only that part of the schema is regenerated. This optimization saves time and provides stability.

So for example, if you:

  1. Install a package containing Content-Fragment-Model-1 and Content-Fragment-Model-2:

    1. GraphQL types for Model-1 and Model-2 will be generated.
  2. Then modify Content-Fragment-Model-2:

    1. Only the Model-2 GraphQL type will get updated.

    2. Whereas Model-1 will remain the same.

NOTE

This is important to note in case you want to do bulk updates on Content Fragment Models through the REST api, or otherwise.

The schema is served through the same endpoint as the GraphQL queries, with the client handling the fact that the schema is called with the extension GQLschema. For example, performing a simple GET request on /content/cq:graphql/global/endpoint.GQLschema will result in the output of the schema with the Content-type: text/x-graphql-schema;charset=iso-8859-1.

Fields

Within the schema there are individual fields, of two basic categories:

  • Fields that you generate.

    A selection of Field Types are used to create fields based on how you configure your Content Fragment Model. The field names are taken from the Property Name field of the Data Type.

    • There is also the Render As property to take into consideration, because users can configure certain data types; for example, as either a single line text or a multifield.
  • GraphQL for AEM also generates a number of helper fields.

    These are used to identify a Content Fragment, or to get more information about a content fragment.

Field Types

GraphQL for AEM supports a list of types. All the supported Content Fragment Model Data Types and the corresponding GraphQL types are represented:

Content Fragment Model - Data Type GraphQL Type Description
Single Line Text String, [String] Used for simple strings such as author names, location names, etc.
Multi Line Text String Used for outputting text such as the body of an article
Number Float, [Float] Used to display floating point number and regular numbers
Boolean Boolean Used to display checkboxes → simple true/false statements
Date And Time Calendar Used to display date and time in an ISO 8086 format
Enumeration String Used to display an option from a list of options defined at model creation
Tags [String] Used to display a list of Strings representing Tags used in AEM
Content Reference String Used to display the path towards another asset in AEM
Fragment Reference A model type Used to reference another Content Fragment of a certain Model Type, defined when the model was created

Helper Fields

In addition to the data types for user generated fields, GraphQL for AEM also generates a number of helper fields in order to help identify a Content Fragment, or to provide additional information about a Content Fragment.

Path

The path field is used as an identifier in GraphQL. It represents the path of the Content Fragment asset inside the AEM repository. We have chosen this as the identifier of a content fragment, because it:

  • is unique within AEM,
  • can be easily fetched.

The following code will display the paths of all Content Fragments that were created based on the Content Fragment Model Person.

{
  personList {
    items {
      _path
    }
  }
}

To retrieve a single Content Fragment of a specific type, you also need to determine its path first. for example:

{
  personByPath(_path: "/content/dam/path/to/fragment/john-doe") {
    item {
      _path
      firstName
      name
    }
  }
}

See Sample Query - A Single Specific City Fragment.

Metadata

Through GraphQL, AEM also exposes the metadata of a Content Fragment. Metadata is the information that describes a content fragment, such as the title of a content fragment, the thumbnail path, the description of a Content Fragment, the date it was created, amongst others.

Because Metadata is generated through the Schema Editor and as such does not have a specific structure, the TypedMetaData GraphQL type was implemented to expose the metadata of a Content Fragment. TypedMetaData exposes the information grouped by the following scalar types:

Field
stringMetadata:[StringMetadata]!
stringArrayMetadata:[StringArrayMetadata]!
intMetadata:[IntMetadata]!
intArrayMetadata:[IntArrayMetadata]!
floatMetadata:[FloatMetadata]!
floatArrayMetadata:[FloatArrayMetadata]!
booleanMetadata:[BooleanMetadata]!
booleanArrayMetadata:[booleanArrayMetadata]!
calendarMetadata:[CalendarMetadata]!
calendarArrayMetadata:[CalendarArrayMetadata]!

Each scalar type represents either a single name-value pair or an array of name-value pairs, where the value of that pair is of the type it was grouped in.

For example, if you want to retrieve the title of a Content Fragment, we know that this property is a String property, so we would query for all the String Metadata:

To query for metadata:

{
  personByPath(_path: "/content/dam/path/to/fragment/john-doe") {
    item {
      _path
      _metadata {
        stringMetadata {
          name
          value
        }
      }
    }
  }
}

You can view all the metadata GraphQL types if you view the Generated GraphQL schema. All model types have the same TypedMetaData.

NOTE

Difference between normal and array metadata
Keep in mind that StringMetadata and StringArrayMetadata both refer to what is stored in the repository, not how you retrieve them.

So for example, by calling the stringMetadata field, you would receive an array of all the metadata that was stored in the repository as a String , and if you call stringArrayMetadata you would receive an array of all the metadata that was stored in the repository as String[].

See Sample Query for Metadata - List the Metadata for Awards titled GB.

Variations

The _variations field has been implemented to simplify querying the variations that a Content Fragment has. For example:

{
  personByPath(_path: "/content/dam/path/to/fragment/john-doe") {
    item {
      _variations
    }
  }
}

See Sample Query - All Cities with a Named Variation.

GraphQL Variables

GraphQL permits variables to be placed in the query. For more information you can see the GraphQL documentation for GraphiQL.

For example, to get all Content Fragments of type Article that have a specific variation, you can specify the variable variation in GraphiQL.

GraphQL Variables

### query
query GetArticlesByVariation($variation: String!) {
    articleList(variation: $variation) {
        items {
            _path
            author
        }
    }
}
 
### in query variables
{
    "variation": "uk"
}

GraphQL Directives

In GraphQL there is a possibility to change the query based on variables, called GraphQL Directives.

For example there you can include the adventurePrice field in a query for all the AdventureModels, based on a variable includePrice.

GraphQL Directives

### query
query GetAdventureByType($includePrice: Boolean!) {
  adventureList {
    items {
      adventureTitle
      adventurePrice @include(if: $includePrice)
    }
  }
}
 
### in query variables
{
    "includePrice": true
}

Filtering

You can also use filtering in your GraphQL queries to return specific data.

Filtering uses a syntax based on logical operators and expressions.

For example, the following (basic) query filters all persons that have a name of Jobs or Smith:

query {
  personList(filter: {
    name: {
      _logOp: OR
      _expressions: [
        {
          value: "Jobs"
        },
        {
          value: "Smith"
        }
      ]
    }
  }) {
    items {
      name
      firstName
    }
  }
}

For further examples, see:

Querying the GraphQL endpoint from an External Website

To access the GraphQL endpoint from an external website you need to configure the:

CORS Filter

NOTE

For a detailed overview of the CORS resource sharing policy in AEM see Understand Cross-Origin Resource Sharing (CORS).

To access the GraphQL endpoint, a CORS policy must be configured in the customer Git repository. This is done by adding an appropriate OSGi CORS configuration file for the desired endpoint(s).

This configuration must specify a trusted website origin alloworigin or alloworiginregexp for which access must be granted.

For example, to grant access to the GraphQL endpoint for https://my.domain you can use:

{
  "supportscredentials":true,
  "supportedmethods":[
    "GET",
    "HEAD",
    "POST"
  ],
  "exposedheaders":[
    ""
  ],
  "alloworigin":[
    "https://my.domain"
  ],
  "maxage:Integer":1800,
  "alloworiginregexp":[
    ""
  ],
  "supportedheaders":[
    "Origin",
    "Accept",
    "X-Requested-With",
    "Content-Type",
    "Access-Control-Request-Method",
    "Access-Control-Request-Headers"
  ],
  "allowedpaths":[
    "/content/_cq_graphql/global/endpoint.json"
  ]
}

If you have configured a vanity path for the endpoint, you can also use it in allowedpaths.

Referrer Filter

In addition to CORS configuration, a Referrer filter must be configured to allow access from third party hosts.

This is done by adding an appropriate OSGi Referrer Filter configuration file that:

  • specifies a trusted website host name; either allow.hosts or allow.hosts.regexp,
  • grants access for this host name.

For example, to grant access for requests with the Referrer my.domain you can:

{
    "allow.empty":false,
    "allow.hosts":[
      "my.domain"
    ],
    "allow.hosts.regexp":[
      ""
    ],
    "filter.methods":[
      "POST",
      "PUT",
      "DELETE",
      "COPY",
      "MOVE"
    ],
    "exclude.agents.regexp":[
      ""
    ]
}
CAUTION

It remains the customer’s responsibility to:

  • only grant access to trusted domains
  • make sure no sensitive information is exposed
  • not use a wildcard [*] syntax; this will both disable authenticated access to the GraphQL endpoint and also expose it to the entire world.
CAUTION

All the GraphQL schemas (derived from Content Fragment Models that have been Enabled) are readable through the GraphQL endpoint.

This means that you need to ensure that no sensitive data is available, as it could be leaked this way; for example, this includes information that could be present as field names in the model definition.

Authentication

See Authentication for Remote AEM GraphQL Queries on Content Fragments.

FAQs

Questions that have arisen:

  1. Q: “How is the GraphQL API for AEM different from Query Builder API?

    • A:
      The AEM GraphQL API offers total control on the JSON output, and is an industry standard for querying content.
      Moving forward, AEM is planning to invest in the AEM GraphQL API.

Tutorial - Getting Started with AEM Headless and GraphQL

Looking for a hands-on tutorial? Check out Getting Started with AEM Headless and GraphQL end-to-end tutorial illustrating how to build-out and expose content using AEM’s GraphQL APIs and consumed by an external app, in a headless CMS scenario.

On this page