> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Content Fragments & GraphQL

Content Fragments are **structured, channel-neutral content** — text, images, data — stored independently of any page layout. Combined with GraphQL, they enable **headless content delivery** to any frontend: website, mobile app, digital signage, or anything HTTP-compatible.

***

## 1. Content Fragments vs Pages

```mermaid
flowchart TD
    subgraph traditional ["Traditional - Pages"]
        Page["Page"]
        Layout["Layout + Components"]
        Content1["Content"]
        Page --> Layout
        Layout --> Content1
    end

    subgraph headless ["Headless - Content Fragments"]
        CF["Content Fragment"]
        GraphQL["GraphQL API"]
        Website["Website"]
        App["Mobile App"]
        Other["IoT / Signage"]
        CF --> GraphQL
        GraphQL --> Website
        GraphQL --> App
        GraphQL --> Other
    end
```

| Feature       | Pages                              | Content Fragments                  |
| ------------- | ---------------------------------- | ---------------------------------- |
| **Layout**    | Tied to AEM templates              | No layout — pure data              |
| **Delivery**  | AEM renders HTML                   | Client renders (arbitrary formats) |
| **Reuse**     | 1 page = 1 URL                     | 1 fragment = multiple consumers    |
| **Authoring** | Page editor with components        | Content Fragment editor            |
| **API**       | Sling model export (`.model.json`) | GraphQL API                        |

***

## 2. Content Fragment Models

Content Fragment Models define **structure** — like a database schema. Created in configuration:

### Creating a Model

1. Go to **Tools** → **General** → **Content Fragment Models**
2. Select your configuration folder (e.g., **My Site**)
3. Click **Create**
4. Title: **Article**
5. Click **Open** to add fields

### Field Types

| Field Type             | Description                      | Example             |
| ---------------------- | -------------------------------- | ------------------- |
| **Single line text**   | Short text                       | Title, Author name  |
| **Multi line text**    | Long formatted text              | Body, description   |
| **Number**             | Integer or decimal               | Rating, price       |
| **Boolean**            | True/false                       | Featured, published |
| **Date and Time**      | Date/time picker                 | Publish date        |
| **Enumeration**        | Predefined options list          | Category, status    |
| **Tags**               | AEM tags                         | Topic tags          |
| **Content Reference**  | Link to asset/content            | Featured image      |
| **Fragment Reference** | Link to another Content Fragment | Author fragment     |
| **JSON Object**        | Arbitrary JSON                   | Metadata            |
| **Tab Placeholder**    | Tab separator to organize fields | Field grouping      |

### Example: Model `Article`

| Field          | Type               | Configuration             |
| -------------- | ------------------ | ------------------------- |
| Title          | Single line text   | Required                  |
| Slug           | Single line text   | Required, unique          |
| Body           | Multi line text    | Rich text, required       |
| Excerpt        | Multi line text    | Plain text, max 300 chars |
| Featured Image | Content Reference  | Asset reference           |
| Publish Date   | Date and Time      | Date only                 |
| Featured       | Boolean            | Default: false            |
| Author         | Fragment Reference | References Author model   |
| Category       | Enumeration        | tech, business, design    |
| Tags           | Tags               | —                         |

### Example: Model `Author`

Create a separate **Author** model:

| Field  | Type                        |
| ------ | --------------------------- |
| Name   | Single line text (required) |
| Bio    | Multi line text             |
| Avatar | Content Reference           |
| Email  | Single line text            |

***

## 3. Creating Content Fragments

### Via Assets Console

1. Go to **Assets** → **Files**
2. Navigate to content folder (e.g. `/content/dam/mysite/articles`)
3. Click **Create** → **Content Fragment**
4. Select model **Article**
5. Enter title and fill fields
6. Click **Create**

### Fragment Editor

Content Fragment editor displays model fields as a form:

- **Single-line fields** → text input
- **Multi-line fields** → rich text editor or plain text area
- **Fragment references** → picker to select another fragment
- **Tab placeholders** → organize fields into tabs

### Variations

Content Fragments support **variations** — alternate versions of the same content:

- **Master** — default version
- **Named variations** — e.g.: "Summary", "Mobile", "Newsletter"

Each variation can override specific fields while inheriting the rest from Master.

***

## 4. GraphQL API

AEM **automatically generates GraphQL APIs** from Content Fragment Models. No code required — setup model and API is ready.

### GraphQL Endpoint

Endpoint per configuration (not single global endpoint). Typical local SDK pattern:

```text
http://localhost:4502/content/_cq_graphql/<configuration>/endpoint.json
```

Replace `&lt;configuration&gt;` with your site configuration name (e.g. `mysite`). Use **GraphiQL IDE** to explore schema and test queries during development.

### Field Name Mapping

GraphQL field names derive from CF Model field labels. AEM converts to **camelCase** — e.g. field label "Publish Date" becomes `publishDate` in GraphQL. Field names are **case-sensitive** — verify via GraphiQL schema explorer if queries return `null`.

### Basic Queries

**List all articles:**

```graphql
{
  articleList {
    items {
      _path
      title
      slug
      excerpt
      publishDate
      featured
      category
    }
  }
}
```

**Get article by path:**

```graphql
{
  articleByPath(_path: "/content/dam/mysite/articles/getting-started") {
    item {
      title
      slug
      body {
        html
        plaintext
      }
      publishDate
      author {
        name
        bio
      }
    }
  }
}
```

### Filtering

```graphql
{
  articleList(
    filter: {
      category: { _expressions: [{ value: "tech" }] }
      featured: { _expressions: [{ value: true }] }
    }
  ) {
    items {
      title
      slug
    }
  }
}
```

### Sorting and Pagination

```graphql
{
  articleList(
    sort: "publishDate DESC"
    limit: 10
    offset: 0
  ) {
    items {
      title
      publishDate
    }
  }
}
```

### Rich Text Formats

Multi-line text fields with rich text can return multiple formats:

```graphql
{
  articleByPath(_path: "/content/dam/mysite/articles/getting-started") {
    item {
      body {
        html
        markdown
        plaintext
        json
      }
    }
  }
}
```

***

## 5. Persisted Queries

Persisted queries are **pre-defined, cached, server-side queries**. Recommended for production environments.

### Creating Persisted Queries

Persisted queries should be created and managed via AEM GraphQL tooling, then promoted like code/config, and executed via persisted query endpoint.

### Execution

```bash
# On Author (local SDK) — requires authentication
curl -u admin:admin http://localhost:4502/graphql/execute.json/mysite/article-list

# On Publish — no authentication for public queries
curl http://localhost:4503/graphql/execute.json/mysite/article-list
```

### Ad-hoc vs Persisted Queries

| Feature         | Ad-hoc Queries            | Persisted Queries          |
| --------------- | ------------------------- | -------------------------- |
| **Caching**     | Cannot cache (Dispatcher) | Cached (GET request)       |
| **Security**    | Client defines query      | Server controls query      |
| **Performance** | Parsed per request        | Parsed once, executed many |
| **CDN**         | Cannot cache POST         | CDN-cacheable GET          |

Always use **persisted queries** in production. Use ad-hoc queries only during development and in GraphiQL IDE.

***

## 6. Headless Architecture Integration

Typical headless flow with AEM:

```mermaid
sequenceDiagram
    participant Client as Frontend (React/Next.js)
    participant CDN as CDN / Dispatcher
    participant Publish as AEM Publish
    participant Author as AEM Author

    Author->>Publish: Publish CF + Model
    Client->>CDN: GET /graphql/execute.json/mysite/article-list
    alt Cache Hit
        CDN-->>Client: Cached JSON Response
    else Cache Miss
        CDN->>Publish: Forward Request
        Publish-->>CDN: JSON Data + Cache Headers
        CDN-->>Client: JSON Response
    end
```

***

## 7. Content Fragments on Pages (Hybrid)

Content Fragments can also be rendered on AEM Pages using **Content Fragment Core Component**:

1. Add **Content Fragment** component to page
2. Select fragment to display
3. Select fields to render
4. Optional: select variation

Bridges traditional page-based and headless approaches — useful when using CF as data source while needing AEM to render HTML.

***

## 8. Best Practices

| Area                   | Recommendation                                                                                |
| ---------------------- | --------------------------------------------------------------------------------------------- |
| Persisted Query Naming | Use stable names with versioning (e.g. `article-list-v1`)                                     |
| Caching                | Prefer GET persisted queries behind Dispatcher/CDN                                            |
| Query Complexity       | Keep response shape minimal; avoid unbounded list fields                                      |
| Authorization          | Only expose necessary endpoints/queries on Publish                                            |
| CORS                   | Allow only trusted frontend origins (config via `com.adobe.granite.cors.impl.CORSPolicyImpl`) |
| Schema Evolution       | Add fields backward-compatibly; deprecate before deleting                                     |

***

## Summary

- **Content Fragments** are structured, channel-neutral content
- **Content Fragment Models** define schemas (field types, validation)
- Create and edit fragments in Assets console
- **GraphQL API** generated automatically from models — no code required
- Querying supports: filtering, sorting, pagination, rich text formats, fragment references
- **Persisted queries** for production performance and caching
- CF can also be used on Pages with Content Fragment Core Component (hybrid approach)

***

## References

- [Content Fragments Documentation](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/assets/content-fragments/overview)
- [AEM Headless Developer Journey](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/headless/journeys/developer/overview)
