---
title: Recursive queries
slug: graph/docs/recursive-queries
docTags: 
createdAt: 2026-07-30T20:30:20.466Z
---

Use recursive queries in Optimizely Graph to fetch deeply nested or interconnected CMS content (such as a landing page that holds blocks of blocks) in a single request, so you do not have to chain separate calls per depth level.

In content management systems (CMSs), content items are often linked or nested within one another. For example, a landing page might contain content blocks, each containing other nested elements. Graph provides mechanisms to traverse these relationships efficiently.

## Self-recurring queries (`@recursive` directive solution)

Self-recurring queries use the `@recursive` directive to fetch nested content up to a bounded depth, giving you a predictable, client-friendly way to render hierarchical content without writing one query per level.

The `@recursive` directive lets you recursively extend your query with the specified depth while traversing nested relationships. Using the GraphQL client to communicate with Graph is a limited but safer option.

The following example shows the recursive directive:

```graphql
query recursiveQuery {  
  _Content(limit: 100, where: { _metadata: { types: { eq: "StartPage" } } }) {  
    items {  
      ... on StartPage {  
        TestAreas {  
          ...LandingPage  
        }  
      }  
    }  
  }  
}

fragment LandingPage on _IContent {  
  _metadata {  
    displayName  
    types  
  }  
  ... on GroupCardListBlock {  
    GroupedItemContentArea @recursive(depth: 10)  
  }  
  ... on LandingPage {  
    TopContentArea @recursive(depth: 2)  
    MainContentArea @recursive  
  }  
}
```

### Explanation

The list below walks through the two parts of the preceding query so you can map each section to the role it plays.

- **Entry point** – The query starts by fetching `StartPage` content items. `TestAreas` is typed as `ContentArea` to contain nested content items.
- **Fragment** – The `LandingPage` fragment defines the data structure you want to retrieve from each content item.

### `@recursive` directive

Review the directive arguments below to understand how each occurrence of `@recursive` in the example controls the depth and shape of the nested response.

- `GroupedItemContentArea @recursive(depth: 10)` – Instructs Graph to fetch nested content within `GroupedItemContentArea` up to a depth of 10 levels. The allowed depth is between one and 10.
- `TopContentArea @recursive(depth: 2)` – Similar to the previous example, but limited to two levels of nesting.
- `MainContentArea @recursive` – Has no specified depth, so Graph fetches nested content within `MainContentArea` by one level (the default value).
- Apply the `@recursive` directive to a field that returns the same type as its associated fragment. For example, content areas such as `GroupedItemContentArea`, `TopContentArea`, and `MainContentArea` are suitable for the `@recursive` directive because they all return `_IContent`. Verify this by checking the **Documentation Explorer** on GraphiQL.

![](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/6FmQBxnz3wuuMcUWAOwVy_07cf671-image.png)

### Key points

The following benefits help you decide when to reach for `@recursive` instead of issuing multiple flat queries.

- **Flexibility** – Control the recursion depth for different fields.
- **Efficiency** – Recursive queries are more efficient than multiple round trips to fetch nested data.
- **Cycle prevention** – The depth argument prevents infinite loops.

## Circular-referenced queries (cyclic fragments)

Circular-referenced queries offer a more natural way to retrieve nested blocks when you do not want to declare a fixed depth: reuse a fragment as either self-referenced (A -> A) or cross-referenced (A -> B -> C -> A) so Graph follows the link graph in your indexed content.

Reuse the fragment definitions as either self-referenced (A -> A) or cross-referenced (A -> B -> C -> A) to retrieve the nested data.

The following example shows a cyclic query:

```graphql
query cyclicQuery {  
  _Content(limit: 100, where: { _metadata: { types: { eq: "StartPage" } } }) {  
    items {  
      ... on StartPage {  
        TestAreas {  
          ...LandingPage  
        }  
      }  
    }  
  }  
}

fragment LandingPage on _IContent {  
  _metadata {  
    displayName  
    types  
  }  
  ... on GroupCardListBlock {  
    GroupedItemContentArea {  
      ...LandingPage  
    }  
  }  
  ... on LandingPage {  
    TopContentArea {  
      ...LandingPage  
    }  
    MainContentArea {  
      ...LandingPage  
    }  
  }  
}
```

### Explanation

The notes below contrast the cyclic example with the earlier `@recursive` query and call out the placement rules you must follow for cyclic fragments.

- This query and fragment are identical to the recursive query.
- The difference is that the `LandingPage` fragment reference in the query has no depth information, so the recursion is unbounded in theory. In practice, it depends on the indexed data, which has 10 levels of depth.
- Place the `...LandingPage` lines under fields that share the same data type as the associated fragment, which is `_IContent` in this case. The example places it correctly inside `GroupedItemContentArea`, `TopContentArea`, and `MainContentArea`, all of which return `_IContent`, similar to the placement requirements for the `@recursive` directive.

### Important considerations

Weigh the following trade-offs before you choose cyclic fragments over the `@recursive` directive.

- **Performance** – Be mindful of the performance implications, especially when dealing with deeply nested content.
- **Compliance** – Although a cyclic query is an easier and more flexible method, it might not be compatible with your GraphQL client.
