AEM GraphQL API for use with Content Fragments graphql-api-for-use-with-content-fragments
Learn how to use Content Fragments in Adobe Experience Manager (AEM) as a Cloud Service with the AEM GraphQL API for headless content delivery.
AEM as a Cloud Service 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.
- AEM Commerce consumes data from a Commerce platform via GraphQL.
- AEM Content Fragments work together with the AEM GraphQL API (a customized implementation, based on standard GraphQL), to deliver structured content for use in your applications.
The GraphQL API 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 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-terminology
GraphQL uses the following:
-
- Schemas are generated by AEM based on the Content Fragment Models.
- Using your schemas, GraphQL presents the types and operations allowed for the GraphQL for AEM implementation.
-
-
The path in AEM that responds to GraphQL queries, and provides access to the GraphQL schemas.
-
See Enabling your GraphQL Endpoint for further details.
-
See the (GraphQL.org) Introduction to GraphQL for comprehensive details, including the Best Practices.
GraphQL Query Types graphql-query-types
With GraphQL you can perform queries to return either:
-
A single entry
AEM provides capabilities to convert queries (both types) to Persisted Queries, that can be cached by Dispatcher and the CDN.
GraphQL Query Best Practices (Dispatcher and CDN) graphql-query-best-practices
The Persisted Queries are the recommended method to be used on publish instances as:
- they are cached
- they are managed centrally by AEM as a Cloud Service
GraphQL queries using POST requests are not recommended as they are not cached, so on a default instance the Dispatcher is configured to block such queries.
While GraphQL also supports GET requests, these can hit limits (for example, the length of the URL) that can be avoided using Persisted Queries.
See Enable caching of persisted queries for further details.
- Create a Cloud Manager environment variable called
ENABLE_GRAPHQL_ENDPOINT
- with the value
true
GraphiQL IDE graphiql-ide
You can test and debug GraphQL queries using the GraphiQL IDE.
Use Cases for Author, Preview and Publish use-cases-author-preview-publish
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)
-
Preview environment; used to:
- Preview queries prior to deploying on the Publish environment
- Query data for JS application (standard use-case)
- Preview queries prior to deploying on the Publish environment
-
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 permission
The permissions are those required for accessing Assets.
GraphQL queries are executed with the permission of the AEM user of the underlying request. If the user does not have read access to some fragments (stored as Assets), they will not become part of the result set.
Also, the user must have access to a GraphQL endpoint to be able to execute GraphQL queries.
Schema Generation 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 must 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.
For example, if a user created a Content Fragment Model called Article
, then AEM generates a GraphQL type ArticleModel
. The fields within this type correspond to the fields and data types defined in the model. In addition, it creates some entrypoints for the queries that operate on this type, such as articleByPath
or articleList
.
-
A Content Fragment Model:
-
The corresponding GraphQL schema (output from GraphiQL automatic documentation):
This shows that the generated type
ArticleModel
contains several fields.-
Three of them have been controlled by the user:
author
,main
andreferencearticle
. -
The other fields were added automatically by AEM, and represent helpful methods to provide information about a certain Content Fragment; in this example, (the helper fields)
_path
,_metadata
,_variations
.
-
-
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 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:
-
Install a package containing
Content-Fragment-Model-1
andContent-Fragment-Model-2
:- GraphQL types for
Model-1
andModel-2
are generated.
- GraphQL types for
-
Then modify
Content-Fragment-Model-2
:-
Only the
Model-2
GraphQL type will get updated. -
Whereas
Model-1
will remain the same.
-
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
.
Schema Generation - Unpublished Models schema-generation-unpublished-models
When Content Fragments are nested it can happen that a parent Content Fragment Model is published, but a referenced model is not.
When this happens, AEM generates an incomplete Schema for the parent Content Fragment Model. This means that the Fragment Reference, which is dependent on the unpublished model, is removed from the schema.
Fields fields
Within the schema there are individual fields, of two basic categories:
-
Fields that you generate.
A selection of Data 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 tab.
- There is also the Render As setting to take into consideration, as users can configure certain data types. For example, a single line text field can be configured to contain multiple single line texts by choosing
multifield
from the drop-down list.
- There is also the Render As setting to take into consideration, as users can configure certain data types. For example, a single line text field can be configured to contain multiple single line texts by choosing
-
GraphQL for AEM also generates several helper fields.
Data Types data-types
GraphQL for AEM supports a list of types. All the supported Content Fragment Model Data Types and the corresponding GraphQL types are represented:
String
, [String]
String
, [String]
Float
, [Float]
Boolean
Calendar
onlyDate
, onlyTime
, dateTime
String
[String]
String
, [String]
Single field:
Model
- Model type, referenced directlyMultifield, with one referenced type:
[Model]
- Array of type Model
, referenced directly from arrayMultifield, with multiple referenced types:
[AllFragmentModels]
- Array of all model types, referenced from array with union typeHelper Fields helper-fields
In addition to the data types for user generated fields, GraphQL for AEM also generates several helper fields to help identify a Content Fragment, or to provide additional information about a Content Fragment.
These helper fields are marked with a preceding _
to distinguish between what has been defined by the user and what has been auto-generated.
Path path
The path field is used as an identifier in AEM 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 Author
, as provided by the WKND tutorial.
{
authorList {
items {
_path
}
}
}
To retrieve a single Content Fragment of a specific type, you also need to determine its path first. For example:
{
authorByPath(_path: "/content/dam/wknd-shared/en/contributors/sofia-sj-berg") {
item {
_path
firstName
lastName
}
}
}
See Sample Query - A Single Specific City Fragment.
Metadata 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:
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:
{
authorByPath(_path: "/content/dam/wknd-shared/en/contributors/sofia-sj-berg") {
item {
_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
.
Keep in mind that
StringMetadata
and StringArrayMetadata
both refer to what is stored in the repository, not how you retrieve them.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 variations
The _variations
field has been implemented to simplify querying the variations that a Content Fragment has. For example:
{
authorByPath(_path: "/content/dam/wknd-shared/en/contributors/ian-provo") {
item {
_variations
}
}
}
_variations
field does not contain a master
variation, as technically the original data (referenced as Master in the UI) is not considered an explicit variation.See Sample Query - All Cities with a Named Variation.
GraphQL Variables graphql-variables
GraphQL permits variables to be placed in the query. For more information, see GraphQL documentation for Variables.
For example, to get all Content Fragments of type Author
in a specific variation (if available), you can specify the argument variation
in GraphiQL.
Query:
query($variation: String!) {
authorList(variation: $variation) {
items {
_variation
lastName
firstName
}
}
}
Query Variables:
{
"variation": "another"
}
This query will return the full list of authors. Authors without the another
variation will fall back to the original data (_variation
will report master
in this case).
Apply a filter, if you want to restrict the list to authors that provide the specified variation (and skip authors that would fall back to the original data):
query($variation: String!) {
authorList(variation: $variation, filter: {
_variation: {
_expressions: {
value: $variation
}
}
}) {
items {
_variation
lastName
firstName
}
}
}
GraphQL Directives 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
.
Query:
query GetAdventureByType($includePrice: Boolean!) {
adventureList {
items {
title
price @include(if: $includePrice)
}
}
}
Query Variables:
{
"includePrice": true
}
Filtering filtering
You can also use filtering in your GraphQL queries to return specific data.
Filtering uses a syntax based on logical operators and expressions.
The most atomic part is a single expression that can be applied to the content of a certain field. It compares the content of the field with a given constant value.
For example, the expression
{
value: "some text"
_op: EQUALS
}
would compare the content of the field with the value some text
and succeeds if the content equals the value. Otherwise, the expression will fail.
The following operators can be used to compare fields to a certain value:
EQUALS
String
, ID
, Boolean
EQUALS_NOT
String
, ID
CONTAINS
String
{ value: "mas", _op: CONTAINS }
will match Christmas
, Xmas
, master
, …)CONTAINS_NOT
String
STARTS_WITH
ID
{ value: "/content/dam/", _op: STARTS_WITH
will match /content/dam/path/to/fragment
, but not /namespace/content/dam/something
EQUAL
Int
, Float
UNEQUAL
Int
, Float
GREATER
Int
, Float
GREATER_EQUAL
Int
, Float
LOWER
Int
, Float
LOWER_EQUAL
Int
, Float
AT
Calendar
, Date
, Time
NOT_AT
Calendar
, Date
, Time
BEFORE
Calendar
, Date
, Time
AT_OR_BEFORE
Calendar
, Date
, Time
AFTER
Calendar
, Date
, Time
AT_OR_AFTER
Calendar
, Date
, Time
Some types also let you specify additional options that modify how an expression is evaluated:
_ignoreCase
String
time
matches TIME
, time
, tImE
, …_sensitiveness
Float
float
values to be considered the same (to work around technical limitations due to the internal representation of float
values; should be avoided, as this option might have a negative impact on performanceExpressions can be combined to a set with the help of a logical operator (_logOp
):
OR
- the set of expressions will succeed if at least one expression succeedsAND
- the set of expressions will succeed if all expressions succeed (default)
Each field can be filtered by its own set of expressions. The expression sets of all fields mentioned in the filter argument will eventually be combined by its own logical operator.
A filter definition (passed as the filter
argument to a query) contains:
- A sub-definition for each field (the field can be accessed through its name, for example, there’s a
lastName
field in the filter for thelastName
field in the Data (field) Type) - Each sub-definition contains the
_expressions
array, providing the expression set, and the_logOp
field that defines the logical operator the expressions should be combined with - Each expression is defined by the value (
value
field) and the operator (_operator
field) the content of a field should be compared to
You can omit _logOp
if you want to combine items with AND
and _operator
if you want to check for equality, as these are the default values.
The following example demonstrates a full query that filters all persons that have a lastName
of Provo
or containing sjö
, independent of the case:
{
authorList(filter: {
lastname: {
_logOp: OR
_expressions: [
{
value: "sjö",
_operator: CONTAINS,
_ignoreCase: true
},
{
value: "Provo"
}
]
}
}) {
items {
lastName
firstName
}
}
}
While you can also filter on nested fields, it is not recommended, as it might lead to performance issues.
For further examples, see:
-
details of the GraphQL for AEM extensions
-
Sample Queries using this Sample Content and Structure
- And the Sample Content and Structure prepared for use in sample queries
Sorting sorting
This feature lets you sort the query results according to a specified field.
The sorting criteria:
-
is a comma separated list of values representing the field path
- the first field in the list will define the primary sort order, the second field is used if two values of the primary sort criterion are equal, the third one if the first two criteria are equal, and so on.
- dotted notation, that is, field1.subfield.subfield and so on…
-
with an optional order direction
- ASC (ascending) or DESC (descending); as default ASC is applied
- the direction can be specified per field; this means that you can sort one field in ascending order, another one in descending order (name, firstName DESC)
For example:
query {
authorList(sort: "lastName, firstName") {
items {
firstName
lastName
}
}
}
And also:
{
authorList(sort: "lastName DESC, firstName DESC") {
items {
lastName
firstName
}
}
}
You can also sort on a field within a nested fragment, using the format of nestedFragmentname.fieldname
.
For example:
query {
articleList(sort: "authorFragment.lastName") {
items {
title
authorFragment {
firstName
lastName
birthDay
}
slug
}
}
}
Paging paging
This feature lets you perform paging on query types that returns a list. Two methods are provided:
offset
andlimit
in aList
queryfirst
andafter
in aPaginated
query
List query - offset and limit list-offset-limit
In a ...List
query you can use offset
and limit
to return a specific subset of results:
offset
: Specifies the first data set to returnlimit
: Specifies the maximum number of data sets to be returned
For example, to output the page of results containing up to five articles, starting from the fifth article from the complete results list:
query {
articleList(offset: 5, limit: 5) {
items {
authorFragment {
lastName
firstName
}
}
}
}
-
Paging requires a stable sort order to work correctly across multiple queries requesting different pages of the same result set. By default it uses the repository path of each item of the result set to make sure the order is always the same. If a different sort order is used, and if that sorting cannot be done at JCR query level, then there is a negative performance impact as the entire result set must be loaded into memory before the pages can be determined.
-
The higher the offset, the more time it will take to skip the items from the complete JCR query result set. An alternative solution for large result sets is to use the Paginated query with
first
andafter
method.
Paginated query - first and after paginated-first-after
The ...Paginated
query type reuses most of the ...List
query type features (filtering, sorting), but instead of using offset
/limit
arguments, it uses the first
/after
arguments as defined by the GraphQL Cursor Connections Specification. You can find a less formal introduction in the GraphQL introduction.
first
: Then
first items to return.
The default is50
.
The maximum is100
.after
: The cursor that determines the beginning of the requested page; note that the item represented by the cursor is not included in the result set; the cursor of an item is determined by thecursor
field of theedges
structure.
For example, output the page of results containing up to five adventures, starting from the given cursor item in the complete results list:
query {
adventurePaginated(first: 5, after: "ODg1MmMyMmEtZTAzMy00MTNjLThiMzMtZGQyMzY5ZTNjN2M1") {
edges {
cursor
node {
title
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
-
By default, paging uses the UUID of the repository node representing the fragment for ordering to ensure the order of results is always the same. When
sort
is used, the UUID is implicitly used to ensure a unique sort; even for two items with identical sort keys. -
Due to internal technical constraints, performance will degrade if sorting and filtering is applied on nested fields. Therefore it is recommended to use filter/sort fields stored at root level. This is also the recommended way if you want to query large paginated result sets.
Web-optimized image delivery in GraphQL queries web-optimized-image-delivery-in-graphql-queries
Web-optimized image delivery lets you use a Graphql query to:
-
Request a URL to a DAM asset image (referenced by a Content Reference)
-
Pass parameters with the query, so that a specific rendition of the image is automatically generated and returned
note note NOTE The rendition specified is not stored in AEM Assets. The rendition is generated and held in cache for a short period. -
Return the URL as part of the JSON delivery
You can use AEM to:
- Pass Web-Optimized Image Delivery into GraphQL queries.
This means that the commands get applied during query execution, in the same way as URL parameters on GET requests for those images.
This lets you dynamically create image renditions for JSON delivery, which avoids having to manually create and store those renditions in the repository.
The solution in GraphQL means you can:
-
Request a URL: use
_dynamicUrl
on theImageRef
reference -
Pass parameters: add
_assetTransform
to the list header where your filters are defined
_dynamicUrl
: a DAM asset_dmS7Url
: a Dynamic Media asset
_dmS7Url
will be null
. See Dynamic Media asset delivery by URL in GraphQL queries.Structure of the Transformation Request structure-transformation-request
AssetTransform
(_assetTransform
) is used to make the URL transformation requests.
The structure and syntax is:
-
format
: an enumeration with all supported formats by its extension: GIF, PNG, PNG8, JPG, PJPG, BJPG, WEBP, WEBPLL or WEBPLY -
seoName
: a string that is used as file name instead of the node name -
crop
: a frame sub structure, if width or height is omitted then the height or width is used as the same valuexOrigin
: the x origin of the frame and is mandatoryyOrigin
: the y origin of the frame and is mandatorywidth
: the width of the frameheight
: the height of the frame
-
size
: a dimension sub structure, if width or height is omitted then the height or width is used as the same valuewidth
: the width of the dimensionheight
: the height of the dimension
-
rotation
: an enumeration of all supported rotations: R90, R180, R270 -
flip
: an enumeration of HORIZONTAL, VERTICAL, HORIZONTAL_AND_VERTICAL -
quality
: an integer from 1–100 notating the percentage of the image quality -
width
: an integer that defines the width of the output image but is ignored by the Image Generator -
preferWebp
: a boolean that indicates if webp is preferred (default value is false)
The URL transform is available for all query types: by path, list or paginated.
Web-optimized image delivery with full parameters web-optimized-image-delivery-full-parameters
The following is a sample query with a full set of parameters:
{
articleList(
_assetTransform: {
format:GIF
seoName:"test"
crop:{
xOrigin:10
yOrigin:20
width:50
height:45
}
size:{
height:100
width:200
}
rotation:R90
flip:HORIZONTAL_AND_VERTICAL
quality:55
width:123
preferWebp:true
}
) {
items {
_path
featuredImage {
... on ImageRef {
_dynamicUrl
}
}
}
}
}
Web-optimized image delivery with a single query variable web-optimized-image-delivery-single-query-variable
The following example shows the use of a single query variable:
query ($seoName: String!) {
articleList(
_assetTransform: {
format:GIF
seoName:$seoName
crop:{
xOrigin:10
yOrigin:20
width:50
height:45
}
size:{
height:100
width:200
}
rotation:R90
flip:HORIZONTAL_AND_VERTICAL
quality:55
width:123
preferWebp:true
}
) {
items {
_path
featuredImage {
... on ImageRef {
_dynamicUrl
}
}
}
}
}
Web-optimized image delivery with multiple query variables web-optimized-image-delivery-multiple-query-variables
The following example shows the use of multiple query variables:
query ($seoName: String!, $format: AssetTransformFormat!) {
articleList(
_assetTransform: {
format:$format
seoName:$seoName
crop:{
xOrigin:10
yOrigin:20
width:50
height:45
}
size:{
height:100
width:200
}
rotation:R90
flip:HORIZONTAL_AND_VERTICAL
quality:55
width:123
preferWebp:true
}
) {
items {
_path
featuredImage {
... on ImageRef {
_dynamicUrl
}
}
}
}
}
Web-optimized image delivery request by URL web-optimized-image-delivery-request-url
If you save your query as a persisted query (for example, with the name dynamic-url-x
) you can then execute the persisted query directly.
For example, to directly execute the previous samples (saved as persisted queries), use the following URLs:
-
Single Parameter; Persisted Query named
dynamic-url-x
-
http://localhost:4502/graphql/execute.json/wknd-shared/dynamic-url-x;seoName=xxx
The response will look like:
-
-
Multiple Parameters; Persisted Query named
dynamic
-
http://localhost:4502/graphql/execute.json/wknd-shared/dynamic;seoName=billiboy;format=GIF;
note caution CAUTION The trailing ;
is mandatory to cleanly terminate the list of parameters.
-
Limitations of web-optimized image delivery web-optimized-image-delivery-limitations
The following limitations exist:
-
Modifiers applied to all images part of the query (global parameters)
-
Caching headers
- No caching on author
- Caching on publish - max-age of 10 minutes (cannot be changed by client)
Dynamic Media asset delivery by URL in GraphQL queries dynamic-media-asset-delivery-by-url
GraphQL for AEM Content Fragments allows you to request a URL to an AEM Dynamic Media (Scene7) asset (referenced by a Content Reference).
The solution in GraphQL means you can:
-
use
_dmS7Url
on theImageRef
reference -
use
_dmS7Url
on multiple references;ImageRef
,MultimediaRef
andDocumentRef
-
use
_dmS7Url
with Smart Crop functionality-
The
_smartCrops
property exposes the Smart Crop configurations available for a specific asset -
see Sample query for Dynamic Media asset delivery by URL - with Smart Crop
-
dam:scene7File
and dam:scene7Domain
attributes on the asset’s metadata when it is created._dmS7Url
: a Dynamic Media asset_dynamicUrl
: a DAM asset
_dynamicURL
will be null
. See web-optimized image delivery in GraphQL queries.Sample query for Dynamic Media asset delivery by URL - Image Reference sample-query-dynamic-media-asset-delivery-by-url-imageref
The following is a sample query:
- for multiple Content Fragments of type
team
andperson
, returning anImageRef
query allTeams {
teamList {
items {
_path
title
teamMembers {
fullName
profilePicture {
__typename
... on ImageRef{
_dmS7Url
height
width
}
}
}
}
}
}
Sample query for Dynamic Media asset delivery by URL - Multiple References sample-query-dynamic-media-asset-delivery-by-url-multiple-refs
The following is a sample query:
- for multiple Content Fragments of type
team
andperson
, returning anImageRef
,MultimediaRef
andDocumentRef
:
query allTeams {
teamList {
items {
_path
title
teamMembers {
fullName
profilePicture {
__typename
... on ImageRef{
_dmS7Url
height
width
}
}
featureVideo {
__typename
... on MultimediaRef{
_dmS7Url
size
}
}
about-me {
__typename
... on DocumentRef{
_dmS7Url
_path
}
}
}
}
}
}
Sample query for Dynamic Media asset delivery by URL - with Smart Crop sample-query-dynamic-media-asset-delivery-by-url-smart-crop
The following is a sample query:
- to expose the Smart Crop configurations available for the requested assets
query allTeams {
teamList {
items {
title
teamMembers {
profilePicture {
... on ImageRef {
height
width
_dmS7Url
_smartCrops {
width
height
name
}
}
}
}
}
}
}
GraphQL for AEM - Summary of Extensions graphql-extensions
The basic operation of queries with GraphQL for AEM adhere to the standard GraphQL specification. For GraphQL queries with AEM there are a few extensions:
-
If you require a single result:
- use the model name; for example, city
-
If you expect a list of results:
- add
List
to the model name; for example,cityList
- See Sample Query - All Information about All Cities
You can then:
-
ASC
: ascendingDESC
: descending
-
Return a page of results using either:
- add
-
The filter
includeVariations
is included in theList
andPaginated
query types. To retrieve Content Fragment Variations in the query results, then theincludeVariations
filter must be set totrue
.note caution CAUTION The filter includeVariations
and the system-generated field_variation
cannot be used together in the same query definition. -
If you want to use a logical OR:
- use
_logOp: OR
- See Sample Query - All Persons that have a name of “Jobs” or “Smith”
- use
-
Logical AND also exists, but is (often) implicit
-
You can query on field names that correspond to the fields within the Content Fragment Model
-
In addition to the fields from your model, there are some system-generated fields (preceded by underscore):
-
For content:
-
_locale
: to reveal the language; based on Language Manager -
_metadata
: to reveal metadata for your fragment -
_model
: allow querying for a Content Fragment Model (path and title) -
_path
: the path to your Content Fragment within the repository -
_reference
: to reveal references; including inline references in the Rich Text Editor -
_variation
: to reveal specific Variations within your Content Fragmentnote note NOTE If the given variation does not exist for a Content Fragment, then the master variation is returned as a (fallback) default. note caution CAUTION The system-generated field _variation
cannot be used together with the filterincludeVariations
.
-
-
For image delivery:
-
_authorURL
: the full URL to the image asset on AEM Author -
_publishURL
: the full URL to the image asset on AEM Publish -
For web-optimized image delivery (of DAM assets):
-
_dynamicUrl
: the full URL to the web-optimized DAM asset on theImageRef
referencenote note NOTE _dynamicUrl
is the preferred URL to use for web-optimized DAM assets and should replace the use of_path
,_authorUrl
, and_publishUrl
whenever possible. -
_assetTransform
: to pass parameters on the list header where your filters are defined -
See:
-
-
_dmS7Url
: on theImageRef
reference for the delivery of the URL to a Dynamic Media asset
-
-
_tags
: to reveal the IDs of Content Fragments or Variations that contain tags; this is an array ofcq:tags
identifiers.- See Sample Query - Names of All Cities Tagged as City Breaks
- See Sample Query for Content Fragment Variations of a given Model that have a specific tag attached
- See Sample Query with filtering by _tags ID and excluding variatons
- See Sample Query with filtering by _tags ID and including variatons
note note NOTE Tags can also be queried by listing the Metadata of a Content Fragment. -
And operations:
-
_operator
: apply specific operators;EQUALS
,EQUALS_NOT
,GREATER_EQUAL
,LOWER
,CONTAINS
,STARTS_WITH
-
_apply
: to apply specific conditions; for example,AT_LEAST_ONCE
-
_ignoreCase
: to ignore the case when querying
-
-
-
GraphQL union types are supported:
-
Fallback when querying nested fragments:
- If a given variation does not exist in a nested fragment, then the Master variation would be returned.
Querying the GraphQL endpoint from an External Website query-graphql-endpoint-from-external-website
To access the GraphQL endpoint from an external website you need to configure the:
Authentication authentication
See Authentication for Remote AEM GraphQL Queries on Content Fragments.
Automated Testing automated-testing
When running a deployment pipeline in AEM Cloud Manager, automated tests are run during pipeline execution.
To provide accurate results, your AEM as a Cloud Service Stage environment should mirror your Production environment as closely as possible. This is especially important for content.
You can achieve this by using the AEM as a Cloud Service Content Copy Tool to copy your Production content to the Stage environment.
Limitations limitations
To protect against potential problems there are default limitations imposed on your queries:
- The query cannot contain more than 1M (1024 * 1024) characters
- The query cannot contain more than 15000 tokens
- The query cannot contain more than 200000 whitespace tokens
You also need to aware of:
-
A field conflict error will be returned when your GraphQL query contains fields with the same name in two (or more) models, and the following conditions are met:
-
So where:
- Two (or more models) are used as possible references; when they are defined as an allowed Model Type in the Content Fragment reference.
and:
- These two models have fields having a common name; that means the same name occurs in both models.
and
- Those fields are of different data types.
-
For example:
-
When two (or more) fragments with different models (for example,
M1
,M2
) are used as possible references (Content Reference or Fragment Reference) from another fragment; for example,Fragment1
MultiField/List
-
And these two fragments with different models (
M1
,M2
) have fields with the same name, but different types.
To illustrate:M1.Title
asText
M2.Title
asText/MultiField
-
Then a field conflict error will occur if the GraphQL query contains the
Title
field.
-
-
FAQs faqs
Questions that have arisen:
-
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.”
- A:
Tutorial - Getting Started with AEM Headless and GraphQL tutorial
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.