Skip to main content

Sources as the Data Pipeline

Think of sources as the plumbing between your domain model and the sheet surface. The domain model declares what entity types exist and how they relate. The columns define what users see. Sources sit in between, translating domain relationships into concrete data queries. diagram A sheet configuration’s root structure includes sources alongside columns, views, renderers, formatters, styles, and sortBy. While columns control presentation and formatters control appearance rules, sources control what data enters the sheet in the first place.

Anatomy of a Source

Each source is an object in the sources array with three main parts:
PropertyRequiredPurpose
idYesUnique identifier referenced by other settings
queryYesDefines which entity type to fetch and optional filters
expandNoSpecifies which navigation properties to traverse
entityFactoryNoDefault property values for newly created records
name or titleNoHuman-readable label for the source
The id is a user-defined string that acts as a reference key. It connects the source to the rest of the sheet configuration. Choose descriptive identifiers that reflect the entity type being queried — for example, user_needs rather than source1.

The Query Object

The query property controls data retrieval:
  • from (required) — The entity type name from your domain model. This tells Powersheet which type of work items to load. For example, from: UserNeed fetches all entities of type UserNeed as defined in your domainModelTypes.
  • where (optional) — A filter predicate that narrows the result set. The predicate value can be static or dynamic. Dynamic values use the () => notation to evaluate JavaScript expressions at runtime. This is particularly useful for date-based filtering or parameterized queries.
  • take (optional) — Limits the number of records returned.
  • orderBy (optional) — Controls server-side ordering of results.
Source queries support dynamic value expressions using () => expression syntax. For example, to filter for future dates only: ">": "() => new Date().toISOString()". You can also inject widget parameters: "==": "() => context.parameters.client". See Dynamic Value Expressions for the full reference on the context object and expression patterns.

The Expand Property

The expand property is where source configuration connects most directly to the domain model. Each entry in the expand array specifies a navigation property name — the same name defined in the domain model’s relationship direct.name or back.name fields. Expansion can be nested, letting you traverse multi-level hierarchies:
sources:
  - id: user_needs
    query:
      from: UserNeed
    expand:
      - name: systemRequirements
        expand:
          - name: systemRequirement
In this example, the source starts from UserNeed entities, expands into the systemRequirements association, and then further into each systemRequirement target entity. Each expansion level corresponds to a new row level in the sheet.
Every expansion level triggers additional data retrieval. Deeply nested expansions — three or more levels — increase load time and memory usage. Design your sources to expand only the levels your sheet actually displays.

How Cardinality Shapes Sources

The relationship cardinality defined in your domain model dictates the expand pattern and, by extension, the column binding syntax and UI behavior. This is one of the most important concepts to internalize. diagram

Many-to-One (N:1)

When a relationship is many-to-one — for example, many UserNeed entities belong to one Chapter — the source uses the direct navigation property name (singular form). The expand entry is a single level:
expand:
  - name: chapter
In the columns section, this produces a scalar navigation property: chapter renders as a single-value reference picker, and chapter.title lets you display a read-only property of the referenced entity. The key insight is that N:1 relationships do not create new row levels — they add reference columns to the existing level.

One-to-Many (1:N)

The reverse relationship — one Chapter has many UserNeed children — uses the back navigation property name (plural form):
expand:
  - name: userNeeds
This creates a new row level in the sheet. When the user expands a Chapter row, the child UserNeed rows appear beneath it. The column binding for the child level uses the navigation property name directly (e.g., userNeeds) without dot notation.

Many-to-Many (M:N)

Many-to-many relationships are the most complex case. They involve an association entity that sits between the two related types. The source expand requires two levels — one for the association, one for the target entity:
expand:
  - name: systemRequirements
    expand:
      - name: systemRequirement
The column binding mirrors this two-level structure: systemRequirements.systemRequirement. The association layer (systemRequirements, plural) is the collection of links, while the target layer (systemRequirement, singular) is the actual entity. In the UI, this pattern produces a multi-item reference picker.
The navigation property names in your source expand must exactly match the direct.name or back.name values in your domain model relationships. A mismatch between the model and the source configuration is one of the most common causes of empty sheets. See Entity Types and Relationships and Link Cardinality for details on how relationships define these names.

Entity Factory: Defaults for New Records

The entityFactory property defines default values that Powersheet assigns when a user creates a new record from within the sheet. This is useful for enforcing consistent initial states:
sources:
  - id: user_needs
    query:
      from: UserNeed
    entityFactory:
      Status: "Draft"
      Priority: "Medium"
When a user adds a new UserNeed row, the Status field automatically populates with “Draft” and Priority with “Medium”. Without entityFactory, new records would have empty fields (or whatever defaults the underlying platform provides). Entity factories also support dynamic value expressions:
entityFactory:
  Client: "() => context.parameters.client"
This makes the factory value depend on the widget parameters passed to the sheet at load time, enabling context-sensitive defaults. Entity factories can be defined at any expansion level, not just the root source. Each expand entry can carry its own entityFactory for the child level.

Multiple Sources

A sheet configuration can define multiple sources in the sources array. Each source gets its own id, query, and expansion tree. This allows a single sheet to display data from different root entity types side by side or in separate sections.
sources:
  - id: user_needs
    query:
      from: UserNeed
    expand:
      - name: systemRequirements
        expand:
          - name: systemRequirement

  - id: hazards
    query:
      from: Hazard
    expand:
      - name: riskControls
        expand:
          - name: riskControl
Each source operates independently — it runs its own query and builds its own expansion tree. The columns section then references bindings from any of the configured sources.

The Three-Layer Connection

The relationship between domain model, sources, and columns follows a strict chain:
  1. Domain model declares entity types (e.g., UserNeed, SystemRequirement) and relationships with navigation property names (e.g., direct.name: chapter, back.name: systemRequirements).
  2. Sources reference those navigation property names in expand entries and the entity type names in query.from. The source does not redefine the relationships — it activates specific paths from the domain model.
  3. Columns use dot-separated binding paths built from the same navigation property names. The binding path systemRequirements.systemRequirement.title means: traverse the systemRequirements association, reach the systemRequirement entity, and display its title property.
If the domain model changes a navigation property name, both the source and the column bindings must update to match. This tight coupling is intentional — it ensures that the sheet always reflects the actual data structure.
The interplay between model, source, and column configuration is best verified by loading the sheet in the Powersheet widget and confirming that expansions and column data appear as expected.

Common Misconceptions

“Sources define the schema.” Sources do not define entity types or properties — the domain model does. Sources only specify which parts of the model to query and expand. If you need to add a new entity type, modify the domain model first, then add a source entry. “More expand levels means richer data.” Each expand level adds a data-loading step. Expanding four or five levels deep may produce impressive hierarchy trees but comes at a performance cost. Most practical sheets use one to three expansion levels. “The id must match the entity type name.” The source id is a free-form string. While it is good practice to use a descriptive name that hints at the entity type (e.g., user_needs for UserNeed), there is no technical requirement for the id to match the from value. “Dynamic where clauses run on the server.” The () => expressions in where predicates are evaluated on the client side before the query is sent. This means the expression has access to the browser context (including widget parameters) but cannot reference server-side variables directly.

Putting It All Together

Consider a requirements traceability sheet that displays UserNeed entities at the root, their linked SystemRequirement entities as children, and further nested DesignRequirement entities at the third level:
sources:
  - id: user_needs
    query:
      from: UserNeed
    entityFactory:
      Status: "Draft"
    expand:
      - name: systemRequirements
        expand:
          - name: systemRequirement
            expand:
              - name: designRequirements
                expand:
                  - name: designRequirement

columns:
  title:
    title: User Need
    hasFocus: true
  systemRequirements.systemRequirement:
    title: System Requirement
    list:
      search:
        - objectId
        - title
      createNew: true
  systemRequirements.systemRequirement.title:
    title: SysReq Title
    hasFocus: true
  systemRequirements.systemRequirement.designRequirements.designRequirement:
    title: Design Requirement
  systemRequirements.systemRequirement.designRequirements.designRequirement.title:
    title: DesReq Title
    hasFocus: true
In this configuration, each column binding mirrors the source expansion path. The systemRequirements and designRequirements segments are association-level navigation properties (M:N), while systemRequirement and designRequirement are the target entity-level properties. This pattern — expanding through the association layer then into the entity — is the standard approach for M:N relationships in Powersheet. For practical steps on configuring sources in your own sheets, see the Sheet Configuration Guides. To understand the domain model relationships that sources reference, see Entity Types and Relationships and Link Cardinality. For a side-by-side comparison of the model and sheet configuration layers, see Data Model vs Sheet Configuration. For details on the () => expression syntax used in where clauses and entity factories, see Dynamic Value Expressions.