ComposeIt LogoComposeIt
Advanced Concepts

Data Access & Collections

Master dynamic data binding, the one record = one document architecture, collection filtering, sorting, grouping, nested hierarchies, and collectionId scoping.

ComposeIt is designed from the ground up for high-precision document automation. Understanding how data binds to templates—from simple single-variable replacements to complex multi-tiered nested collections—is essential for building robust reports, invoices, contracts, and statements.


The "One Record = One Document" Architecture

The fundamental mental model of ComposeIt is One Record = One Document:

When you trigger a document render, whether via the ComposeIt REST API, Webhook, Zapier, or the Editor test runner. The payload root object (root) represents exactly one generated document instance.

Data Shaping Best Practices

To ensure predictable document composition, structure your payload so that the document metadata sits on the root object, while all recurring or one-to-many tabular records are represented as JSON arrays of objects.

payload.json
{
  "invoiceNumber": "INV-2026-0042",
  "issueDate": "2026-09-24",
  "dueDate": "2026-10-24",
  "client": {
    "name": "Acme Global Dynamics",
    "email": "billing@acmeglobal.com",
    "country": "Germany",
    "isVatExempt": false
  },
  "summary": {
    "subtotal": 12500.0,
    "taxRate": 0.19,
    "total": 14875.0
  },
  "lineItems": [
    {
      "id": "item-101",
      "sku": "SRV-DEV-01",
      "description": "Custom API Gateway Architecture",
      "hours": 40,
      "rate": 175,
      "discount": 0.1,
      "amount": 6300.0
    },
    {
      "id": "item-102",
      "sku": "SRV-SEC-03",
      "description": "SOC2 Compliance & Pen-testing Audit",
      "hours": 35,
      "rate": 180,
      "discount": 0.0,
      "amount": 6300.0
    }
  ]
}

Batch Generation via API

When generating PDFs in bulk (e.g. 500 invoices at the end of the month), pass an array of these root record objects to the batch rendering endpoint: POST /api/export/batch. Each item in the array is merged against the template independently to produce a dedicated PDF.


Working with Collections

A Collection in ComposeIt turns any visual element or container into a repeated template block. You can bind collections to:

  • Table rows (<tr>) for compact tabular line items
  • Cards & Grids (<div> / flex blocks) for product catalogs or executive profiles
Collection Trait Panel in ComposeIt Editor

Binding a Component to a Collection

  1. Select the component you wish to repeat (e.g. a table row<tr>).
  2. Open the Traits panel on the right sidebar.
  3. Click Edit Logic to select a collection to loop over. Each component type can be looped over by clicking on it then accessing the context menu then clicking Wrap with collection.
  4. In the Data Path selector, enter or select the array path relative to the root (e.g. root.lineItems).
  5. Within the repeated component, bind child text elements directly using path selectors or custom path strings.

Collection Operations: Sorting, Filtering & Grouping

ComposeIt allows you to transform raw data at the collection level without needing backend pre-processing.

Collection Sort Filter and Group Settings

1. Sorting

Configure multi-column sorting directly inside the Collection Traits:

PropertyDescriptionExample
Sort FieldPath to the target sort keyamount or date
DirectionAscending (ASC) or Descending (DESC)DESC
TypeNumber, String, or DateNumber

You can chain multiple sort rules (e.g. Sort by category ASC, then by amount DESC).

2. Filtering

Filters allow you to suppress or display items based on dynamic logic. Filters can be configured using visual rule builders:

Common filtering use cases:

  • Filter out refunded or zero-value transactions.
  • Filter line items by region or department for targeted sub-reports.
  • Separate paid vs. overdue invoices into two separate visual tables on the same page.

3. Grouping

Grouping allows you to split a single flat array into hierarchical sub-collections based on a categorical key.

For example, given a flat list of department expenses:

[
  { "dept": "Engineering", "item": "Cloud Compute", "cost": 4200 },
  { "dept": "Engineering", "item": "CI/CD Pipeline", "cost": 850 },
  { "dept": "Marketing", "item": "AdWords Campaign", "cost": 6100 },
  { "dept": "Marketing", "item": "Conference Booth", "cost": 3400 }
]

When you set Group By to dept, ComposeIt generates:

  1. An outer group repeater with access to group.key (e.g. "Engineering").
  2. An inner items collection containing all matching records for that specific key.
  3. Built-in aggregation helpers like SUM(group.items, "cost").

Conditional Rendering

Every element in ComposeIt can be wrapped in a Condition to dynamically toggle its visibility based on data values.

Condition Rules Editor in ComposeIt

Common Conditions

  • Tax & Discount Row Suppression: Hide the discount row if root.summary.discount == 0.
  • Payment Method Badges: Display a wire transfer instructions block only if root.paymentMethod == "wire".
  • Disclaimers & Legal Notes: Show state-specific compliance clauses based on root.client.state.

Nested Collections & Hierarchies

Complex business documents often require multiple layers of iteration. Typical examples include:

  • Order Statements: Order → Line Items → Product Options / Modifiers
  • Departmental Audits: Division → Cost Centers → Line Item Expenses
  • Event Itineraries: Days → Tracks → Sessions → Speakers
Nested Collections Component Tree

Hierarchical Data Structure Example

nested-order-data.json
{
  "accountName": "Enterprise Logistics Corp",
  "billingPeriod": "Q3 2026",
  "warehouses": [
    {
      "id": "wh-eu-west",
      "name": "Frankfurt Central Hub",
      "manager": "Lena Schmidt",
      "shipments": [
        {
          "id": "ship-901",
          "carrier": "DHL Express",
          "dispatchDate": "2026-08-14",
          "packages": [
            { "trackingNumber": "DHL-88912", "weightKg": 14.5, "fee": 120 },
            { "trackingNumber": "DHL-88913", "weightKg": 8.2, "fee": 85 }
          ]
        },
        {
          "id": "ship-902",
          "carrier": "FedEx Freight",
          "dispatchDate": "2026-08-16",
          "packages": [
            { "trackingNumber": "FX-44019", "weightKg": 120.0, "fee": 450 }
          ]
        }
      ]
    }
  ]
}

Understanding collectionId & Scope Disambiguation

When working with nested repeaters, a common point of confusion in document templating is variable name collisions.

Notice in the JSON above:

  • warehouse has an id ("wh-eu-west")
  • shipment has an id ("ship-901")

If you place a text element inside the inner package repeater and request id, which identifier should be printed? The warehouse ID, the shipment ID, or an error?

How collectionId Resolves Context

To eliminate ambiguity, ComposeIt assigns a unique, immutable collectionId to every collection wrapper component in the canvas.

Collection ID Scope Disambiguation Diagram

Definitive Path Syntax

Inside any nested block, variables can be addressed with absolute precision using the collectionId prefix:

Collection KeyCollection ID
warehousescollReF4xw
shipmentscollGt9dkd
packagescollDkdke9
Scope LevelPath ExpressionResolved Value
Document Rootroot.billingPeriod"Q3 2026"
Outer CollectioncollReF4xw.name"Frankfurt Central Hub"
Outer Collection KeycollReF4xw.id"wh-eu-west"
Mid CollectioncollGt9dkd.carrier"DHL Express"
Mid Collection KeycollGt9dkd.id"ship-901"
Innermost CollectioncollDkdke9.trackingNumber"DHL-88912"
Innermost Collection KeycollDkdke9.fee120

Always Use collectionId in Nested Repeaters

nested collections should always use the full prefix (warehouse.id, shipment.id). This guarantees that template refactoring or changes in JSON payload structure will never cause accidental scope shadowing.

Summary Aggregations Across Scopes

With collectionId, you can perform mathematical aggregations across nested boundary layers:

'Compute total fees for the current shipment'

SUM(shipment.packages, 'fee')

'Display warehouse-level shipment count'
COUNT(warehouse.shipments)

Next Steps

We use cookies to ensure you get the best experience on our website. For more information on how we use cookies, please see our privacy policy.