# aem-headless-client-java
**Repository Path**: mirrors_adobe/aem-headless-client-java
## Basic Information
- **Project Name**: aem-headless-client-java
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-04-16
- **Last Updated**: 2026-05-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# AEM Headless Client for Java
See [aem-headless-client-js](https://github.com/adobe/aem-headless-client-js) for the JavaScript version of this client.
## Setup
### Maven Dependency
Add the following dependency to your classpath:
```xml
com.adobe.aem.headless
aem-headless-client-java
1.2.0
```
The client comes with a transitive dependency to the Jackson JSON library that also needs to be available at runtime:
```xml
com.fasterxml.jackson.core
jackson-databind
```
## Usage
### Creating an AEMHeadless Client
The easiest way to create a client looks as follows:
```java
import com.adobe.aem.graphql.client.AEMHeadlessClient
...
AEMHeadlessClient aemHeadlessClient = new AEMHeadlessClient("http://localhost:4503");
...
```
If a non-standard GraphQL endpoint is used on AEM side, the endpoint may contain a full path:
```java
aemHeadlessClient = new AEMHeadlessClient("http://localhost:4503/content/graphql-custom");
```
For more complex configurations, the builder pattern is available:
```java
AEMHeadlessClient aemHeadlessClient = AEMHeadlessClient.builder().
.endpoint("http://localhost:4503")
// ... further configuration
.build();
```
To create a client with explicitly set timeouts:
```java
AEMHeadlessClient aemHeadlessClient = AEMHeadlessClient.builder().
.endpoint("http://localhost:4503")
.connectTimeout(10000)
.readTimeout(30000)
.build();
```
If timeouts are not set explicitly a default of 15 seconds is used. To disable timeouts (not recommended) use the value `0`.
### Using Authorization
If authorization is required, it can be added using the builder:
```java
// basic auth
AEMHeadlessClient aemHeadlessClient = AEMHeadlessClient.builder().
.endpoint(new URI("http://localhost:4503/content/graphql-custom"))
.basicAuth("user", "password")
.build();
// token auth
AEMHeadlessClient aemHeadlessClient = AEMHeadlessClient.builder().
.endpoint(new URI("http://localhost:4503/content/graphql-custom"))
.tokenAuth("token")
.build();
```
### Running Queries
To execute a simple GraphQL query:
```java
String query = "{\n" +
" articleList{\n" +
" items{ \n" +
" _path\n" +
" } \n" +
" }\n" +
"}";
try {
GraphQlResponse response = aemHeadlessClient.runQuery(query);
JsonNode data = response.getData();
// ... use the data
} catch(AEMHeadlessClientException e) {
// e.getMessage() will contain an error message (independent of type of error)
// if a response was received, e.getGraphQlResponse() will return it (otherwise null)
}
```
It is also possible to pass in query parameters using a simple map as second parameter of `runQuery()`:
```
GraphQlResponse response = client.runQuery(query, Map.of("author", "Ian Provo"));
```
### Mapping results to Java POJOs
To not have to deal with JSON maps and arrays in Java, it is easy to define POJOs and let Jackson (the JSON API used by this library) do the mapping. For non-default mapping any default annotatinos from the Jackson library can be used (e.g. `@JsonSetter`). Results of sub selections can be mapped by using POJOs as member field.
Example POJOs:
```
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Article {
private String path;
private String title;
private Author author;
String getPath() {
return path;
}
@JsonSetter("_path")
void setPath(String path) {
this.path = path;
}
String getTitle() {
return title;
}
void setTitle(String title) {
this.title = title;
}
Author getAuthor() {
return author;
}
@JsonSetter("authorFragment")
void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString() {
return "Article [path=" + path + ", title=" + title + ", author=" + author + "]";
}
public static class Author {
private String firstName;
private String lastName;
String getFirstName() {
return firstName;
}
void setFirstName(String firstName) {
this.firstName = firstName;
}
String getLastName() {
return lastName;
}
void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return firstName + " " + lastName;
}
}
```
To use the automatic mapping, the `GraphQlResponse ` provides the `getItems(Class>)` method:
```java
GraphQlResponse response = aemHeadlessClient.runQuery(query);
List listOfArticles = response.getItems(Article.class);
```
### Using the Query Builder
There is a query builder that simplifies creating the queries using the builder pattern:
```java
GraphQlQuery queryCursorPaging = GraphQlQuery.builder()
.contentFragmentModelName("article")
.field("_path")
.field("title")
.build();
GraphQlResponse response = aemHeadlessClient.runQuery(query);
List data = response.getItems(Article.class);
```
It is also possible to create queries with sub selections and add sorting as follows:
```java
import static com.adobe.aem.graphql.client.GraphQlQueryBuilder.subSelection;
...
GraphQlQuery queryCursorPaging = GraphQlQuery.builder()
.contentFragmentModelName("article")
.field("_path")
.field("title")
.field(subSelection("authorFragment").field("firstName").field("lastName"))
.sortBy("title ASC", "_path DESC")
.build();
```
Sub selections can be nested (the `field` method of the sub selection also sub selections in the same way as the top-level `field` method does).
### Using Filtering
The query builder supports basic filtering as follows:
```java
GraphQlQuery queryWithFiltering = GraphQlQuery.builder()
.contentFragmentModelName("adventure")
.field("_path")
.field("title")
.field("price")
.filter("price", Operator.LOWER, 154)
.sortBy("title ASC")
.build()
```
For simple cases, the filter can be given directly to the field method to keep the code DRY (no need to mention 'price' twice):
```java
import static com.adobe.aem.graphql.client.GraphQlQueryBuilder.filter;
...
GraphQlQuery queryWithFiltering = GraphQlQuery.builder()
.contentFragmentModelName("adventure")
.field("_path")
.field("title")
.field("price", filter(Operator.LOWER, 154))
.sortBy("title ASC")
.build()
```
It is also possible to pass in options as supported by the AEM backend:
```java
import static com.adobe.aem.graphql.client.GraphQlQueryBuilder.ignoreCase;
import static com.adobe.aem.graphql.client.GraphQlQueryBuilder.sensitiveness;
...
GraphQlQuery queryWithFiltering = GraphQlQuery.builder()
.contentFragmentModelName("adventure")
.field("_path")
.field("title", filter(Operator.CONTAINS, "gastronomic", ignoreCase()))
.field("price", filter(Operator.LOWER, 154, sensitiveness(0.1)))
.sortBy("title ASC")
.build()
```
A filter value can also be set by a query variable:
```java
GraphQlQuery queryWithFilterVar = GraphQlQuery.builder()
.contentFragmentModelName("adventure")
.field("_path")
.field("title")
.field("price", filter(Operator.LOWER, Type.Float, "maxPrice"))
.sortBy("title ASC", "_path DESC")
.build();
GraphQlQueryVars vars = GraphQlQueryVars.create();
vars.put("maxPrice", 200);
GraphQlResponse response = client.runQuery(queryWithFilterVar, vars);
...
```
Finally it is also possible to pass the whole filter as variable:
```java
GraphQlQuery queryWithFilter = GraphQlQuery.builder()
.contentFragmentModelName("article")
.field("_path")
.field("title")
.useFilter()
.field(subSelection("authorFragment").field("firstName").field("lastName"))
.sortBy("title ASC", "_path DESC")
.build();
Map filter = new HashMap<>();
Map filterTitle = new HashMap<>();
filter.put("title", filterTitle);
Map filterTitleExpressions = new HashMap<>();
filterTitle.put("_expressions", filterTitleExpressions);
filterTitleExpressions.put("value", "surf");
filterTitleExpressions.put("_operator", "CONTAINS");
filterTitleExpressions.put("_ignoreCase", true);
GraphQlQueryVars vars = GraphQlQueryVars.create().addVar("filter", filter);
GraphQlResponse response = client.runQuery(queryWithFilter, vars);
...
```
### Using Pagination
The query builder supports building queries with pagination:
```java
GraphQlQuery queryCursorPaging = GraphQlQuery.builder()
.contentFragmentModelName("article")
.field("_path")
.field("title")
.paginated()
.sortBy("title ASC")
.build();
```
To run those queries, the client allows to retrieve a paging cursor and use the `hasNext()` and `next()` methods on it:
```java
GraphQlPagingCursor respCursor = client.createPagingCursor(queryCursorPaging, /* page size */ 10);
while (respCursor.hasNext()) {
GraphQlResponse resp = respCursor.next();
for (Article article : resp.getItems(Article.class)) {
// do something with the article
}
}
```
There is also the option to use offset/limit-based pagination - this is supported but whenever possible the cursor based approach above should be preferred. Here is an example of the offset based approach:
```java
GraphQlQuery queryOffsetPaging = GraphQlQuery.builder()
.contentFragmentModelName("article")
.field("_path")
.field("title")
.paginated(GraphQlQuery.PaginationType.OFFSET_LIMIT)
.sortBy("title ASC")
.build();
GraphQlResponse respPagingOffset;
for(int pageNo = 0; (respPagingOffset = client.runQuery(queryOffsetPaging, pageNo * pageSize, pageSize)).hasItems(); pageNo++) {
for (Article article : respPagingOffset.getItems(Article.class)) {
// do something with the article
}
}
```
### Using Persisted Queries
To execute a persisted query the the method `runPersistedQuery` is used:
```java
try {
// for this to work, "/myProj/queryName" needs to be set up on AEM side
GraphQlResponse response = aemHeadlessClient.runPersistedQuery("/myProj/queryName");
JsonNode data = response.getData();
// ... use the data
} catch(AEMHeadlessClientException e) {
// e.getMessage() will contain an error message (independent of type of error)
// if a response was received, e.getGraphQlResponse() return it (otherwise null)
}
```
Also for persisted queries, query parameters can be passed in as second argument:
```
GraphQlResponse response = client.runPersistedQuery("/myProj/articles", Map.of("author", "Ian Provo"));
```
### Inspecting available persisted queries
To list available persisted queries for a configuration name the following snippet can be used:
```java
List queries = client.listPersistedQueries("myProj");
queries.stream().forEach( persistedQuery -> {
/* use e.g. persistedQuery.getShortPath()... or persistedQuery.getQuery() */
});
```
It is best practice to deploy persisted queries as part of the software in content packages as part of the overall configuration in `/conf`.
## API Reference
See generated Javadoc as published to Maven Central.
## Contributing
Contributions are welcome! Read the [Contributing Guide](./.github/CONTRIBUTING.md) for more information.
## Licensing
This project is licensed under the Apache V2 License. See [LICENSE](LICENSE) for more information.