PagibleAI JSON:API Navigation

Use page relationships when you need breadcrumbs, child lists, a site menu or a section subtree. PagibleAI returns each item as a lightweight navs resource, avoiding full page content in every menu entry.

JSON:API places these resources in the flat included array. Their parent_id values preserve the hierarchy. See JSON:API Pages for page filters, pagination, content, files and visibility rules.

Choose a navigation relationship

Frontend needRelationship
Breadcrumb from the root to the current pageancestors
Direct link back to the enclosing pageparent
Cards or links immediately below the current pagechildren
Primary navigation rooted at the top of the page treemenu
Section navigation rooted at the current pagesubtree
A custom recursive child traversalchildren.children with a higher include depth

Request only the relationships you render. The has attribute counts descendants in the stored tree, including descendants that may be hidden from the current user. Check relationship linkage or included resources when you need to know which navigation items were actually returned.

Understand navigation resources

A navigation item has the JSON:API type navs. It can expose these attributes:

  • parent_id, lang, path, name, and title
  • to and domain
  • has, the integer number of descendants in the stored page tree
  • createdAt and updatedAt
  • config, including resolved files when the configuration references media

A navs resource does not contain page content, meta, theme, type, tag, status, or cache. Use the primary pages resource when you need those page attributes.

{
  "type": "navs",
  "id": "0198d0f8-8c1e-75fc-b7fd-e5d9f3a7c1e3",
  "attributes": {
    "parent_id": "0198a4b2-3c5e-7f1a-8d2b-e4f6a8c0d2e4",
    "lang": "en",
    "path": "blog",
    "name": "Blog",
    "title": "Blog",
    "to": "",
    "domain": "",
    "has": 8,
    "config": null
  }
}

Build breadcrumbs

Include ancestors to build a breadcrumb for the current page:

https://example.com/cms/pages/{uuid}?include=ancestors

relationships.ancestors.data lists the ancestor resource identifiers from the root page to the direct parent. Match each identifier with its navs resource in included, preserving the relationship order. The current page is not part of ancestors; append the primary resource when your breadcrumb should include it.

For a root page, the relationship is an empty array.

List direct children

Include children when you need only the pages immediately below the current page:

https://example.com/cms/pages/{uuid}?include=children

Use this relationship for local section navigation, category listings, or a list of articles below a blog page. Each child appears as a navs resource in included.

Restricted or disabled children are omitted for frontend users who may not access them.

Build a site menu

Include menu to retrieve the configured menu subtree rooted at the top of the current page tree:

https://example.com/cms/pages/{uuid}?include=menu

This is useful when every page request must also provide the site's primary navigation. The root page itself is not duplicated as a navigation resource; the relationship contains its descendants within the configured navigation depth.

You can combine the menu with a breadcrumb:

https://example.com/cms/pages/{uuid}?include=ancestors,menu

Get the parent page

Include parent when you need only the direct parent page:

https://example.com/cms/pages/{uuid}?include=parent

relationships.parent.data contains one navs identifier, and the complete navigation resource appears in included. For a root page, relationships.parent.data is null.

Build a section subtree

Include subtree to retrieve descendants below the requested page:

https://example.com/cms/pages/{uuid}?include=subtree

Use this relationship for a section menu or mega-menu that starts at the current page rather than the page-tree root. The result is limited by CMS_NAVDEPTH, whose default value of 2 returns up to two descendant levels.

Items below a disabled ancestor and items blocked by frontend access rules are omitted.

Control navigation depth

Two settings control different kinds of depth:

  • CMS_NAVDEPTH controls how many descendant levels menu and subtree load. The default is 2.
  • CMS_JSONAPI_MAXDEPTH controls how deeply a client may traverse relationship paths. The default is 1.

For example, allow an include depth of at least two before requesting direct children and their children:

https://example.com/cms/pages/{uuid}?include=children,children.children

Increase either value only when your frontend needs the additional data. Deeper requests perform more relationship queries and produce larger responses.

Reduce fields and rebuild the tree

Navigation usually needs only a few attributes. Request separate sparse fieldsets for the page and navigation resources:

https://example.com/cms/pages?filter[tag]=root&include=menu&fields[pages]=name,menu&fields[navs]=parent_id,path,name,title,has

When fields[pages] is present, include menu or the requested relationship name in that fieldset.

Resolve resources in relationship order, then group only those resources by parent_id. This avoids mixing ancestors or another included relationship into the menu:

const page = payload.data?.[0];
const included = new Map(
  (payload.included ?? []).map(resource => [
    `${resource.type}:${resource.id}`,
    resource,
  ]),
);

const menu = (page?.relationships?.menu?.data ?? [])
  .map(reference => included.get(`${reference.type}:${reference.id}`))
  .filter(Boolean);
const menuIds = new Set(menu.map(resource => resource.id));
const childrenByParent = new Map();

for (const resource of menu) {
  const parentId = resource.attributes.parent_id;
  const children = childrenByParent.get(parentId) ?? [];
  children.push(resource);
  childrenByParent.set(parentId, children);
}

const roots = menu.filter(
  resource => !menuIds.has(resource.attributes.parent_id),
);
const childrenOf = parentId => childrenByParent.get(parentId) ?? [];

roots contains the top-level entries of the returned menu, while childrenOf(id) returns the next ordered level. The same approach works for subtree after replacing the relationship name.