Best approach to return hierarchical/grouped JSON from AEM Content Fragments organized in subfolders
Hi all,
I'm working on an AEM 6.5 project and need to serve Content Fragment data as a hierarchical JSON response grouped by subfolder, not just a flat list. Looking for advice on whether my approach is sound or if there's a better way.
Requirement
I have content organized in subfolders under a parent folder in the DAM. Each subfolder represents a "section" of a page, and each section contains multiple content fragments of the same model.
DAM structure:
/content/dam/.../parent-folder/
├── section-alpha/
│ ├── item-1 (CF)
│ └── item-2 (CF)
├── section-beta/
│ └── item-1 (CF)
├── section-gamma/
│ └── item-1 (CF)
└── section-delta/
├── item-1 (CF)
├── item-2 (CF)
└── item-3 (CF)
Expected JSON response (strict contract with frontend):
{
"page_name": {
"section_alpha": {
"items": [
{
"field_a": "value",
"field_b": 2,
"field_c": ["tag1", "tag2"],
"field_d": "some expression",
"field_e": "Title text",
"field_f": "Description text",
"image": { "_publishUrl": "https://..." },
"field_g": "link",
"flag_1": true,
"flag_2": false,
"field_h": "config_value_1",
"field_i": "config_value_2"
}
],
"total_count": 2
},
"section_beta": {
"items": [...],
"total_count": 1
},
"section_gamma": {
"items": [...],
"total_count": 1
},
"section_delta": {
"items": [...],
"total_count": 3
}
}
}
The problem
AEM GraphQL persisted queries return a flat list:
{
"data": {
"myModelList": {
"items": [
{ "_path": "/.../section-alpha/item-1", ... },
{ "_path": "/.../section-alpha/item-2", ... },
{ "_path": "/.../section-beta/item-1", ... },
...
]
}
}
}
There's no native way in AEM GraphQL to:
- Group items by their parent subfolder in the response
- Wrap each group in a named key with a computed count
- Nest the groups under a parent object
The STARTS_WITH filter on _path returns all CFs under a folder, but as a flat array with no subfolder grouping.
My proposed solution
CF Model: Single flat model with all fields (no fragment references needed since the contract is flat).
Persisted query: One query that fetches all items under the parent folder using STARTS_WITH, including _path in the response.
query($path: ID!) {
myModelList(
sort: { priority_field: ASC }
filter: {
_path: {
_expressions: { value: $path, _operator: STARTS_WITH }
}
}
) {
items {
_path
field_a
field_b
# ... all other fields
}
}
}
Custom Sling Servlet that:
- Makes a single internal call to the persisted query with path = /parent-folder/
- Parses the flat GQL response
- Groups items by extracting the subfolder name from each item's
_path(e.g.,_path=.../section-alpha/item-1→ group key =section_alpha) - Strips
_pathfrom each item - Assembles the hierarchical JSON with computed
total_countper group - Returns the final response
My questions
-
Is a custom servlet for post-processing the right approach? Or is there a way to achieve subfolder-based grouping directly in AEM GraphQL that I'm missing?
-
Single broad query vs. N queries per subfolder? I'm leaning toward one broad STARTS_WITH query on the parent + Java-side grouping (1 HTTP call) rather than N separate queries per subfolder. Any pitfalls with large result sets?
-
Alternative: Store the entire structure as raw JSON in a single CF using a JSON field type. This avoids the complexity entirely but loses structured authoring, field-level validation, and content reusability. Anyone found a middle ground?
-
Has anyone built a reusable "CF aggregation" pattern that groups content fragments by folder structure in the response? Would love to see if there's a community-standard approach.
Thanks for any pointers!