Skip to Content
Data Sources & Queries

Data Sources & Queries

The “Data sources” panel in the Puck editor’s left rail, what it is, and how the {{ }} template-token syntax used across SEO and Components Reference → Data actually works. Confirmed by reading @pantheon-systems/puck-css’s source directly, not from the editor UI alone.

This is a genuinely capable, native P1 feature. It significantly changes the answer given on Content Relationships: “list every document of this Template” turns out to be native, with zero custom code. What’s still a real gap is narrower than previously documented there, see that page for the corrected read.

1. Auto-generated per-Template queries. Confirmed directly from css-client’s own Query type doc comment: “A query defines WHAT to retrieve, sort order, pagination defaults. Auto-generated alongside a datasource when a template is created.” Creating a Template (see Templates) automatically gives you a queryable datasource returning every document built from it, with sort order and pagination defaults baked in. No code, no manual setup.

2. User-authored Data Sources. A separate, also-native, editor-UI-driven system for defining custom HTTP JSON datasources, the panel visible in the left rail alongside Blocks. These can point at any HTTP JSON endpoint, external or internal, and are scoped either globally (available to every page) or to one specific page.

Both feed the same {{ }} token-binding syntax at render time, confirmed by the SDK exporting cssQueriesToDatasourceDefinitions, an adapter that turns the auto-generated Template queries into the same shape as a user-authored datasource. They’re unified at the binding layer even though they’re created differently.

Auto-generated Template queries, in detail

QueriesEndpoint (@pantheon-systems/css-client) exposes:

list(siteId, branchId): Promise<Query[]> get(siteId, branchId, name): Promise<Query> getResults(siteId, branchId, queryName, params?): Promise<QueryResults>

A Query carries sort, defaultLimit, maxLimit, includeMetadata, includeSnapshot, and an autoGenerated flag. getResults() accepts limit, offset, includeMetadata, includeSnapshot, nothing else.

No filter parameter, confirmed, not just unobserved. The auto-generated query sorts and paginates every document of a Template, but there’s no way to ask it for a filtered subset (documents matching a specific field value, or a specific editor-picked list of IDs) through this API. Checked the one other place filtering could plausibly live too: DocumentsEndpoint.list()’s own ListDocumentsOptions supports only pathPrefix plus pagination, no arbitrary-field or template filter either. “Show me every Cocktail, newest first” is native. “Show me these 3 specific Cocktails” is not, anywhere in the current API surface, see Content Relationships for what closing that gap would actually take.

The Data Sources panel, in detail

Confirmed from user-remote-datasource-types.d.ts and the store functions backing the panel:

type HttpJsonRemoteDatasourceDefinition = { id: string; // lowercase_snake_case label: string; description: string; urlTemplate: string; // supports {{ urlParams.id }} etc. headers?: Record<string, string>; query?: Record<string, string>; fields: { path: string; description: string }[]; }; type RemoteDatasourceScope = "global" | "page";

listGlobalRemoteDatasources / upsertGlobalRemoteDatasource / deleteGlobalRemoteDatasource and their page-scoped equivalents back the panel’s Save/list/delete behavior. These run through the same store abstraction (createP1PageStore, ensureInitialized, getSharedP1Client) used for page content itself, backed by real P1 credentials in this project’s own route handler (app/p1/api/[...p1]/route.ts’s createP1Handler call). That’s strong evidence these persist server-side and are shared across the team, not scoped to one browser, though we haven’t independently confirmed two different editors see the same saved datasource in real time.

The {{ }} token syntax, in full

More capable than what’s documented in SEO’s template-token note alone:

  • {{ source.field }} in any text field. After typing {{, matching datasource paths are suggested automatically.
  • Nested keys use dots: {{ source.nested.key }}.
  • Allowlisted helper functions, confirmed from the SDK’s own function table: trim, toLowerCase, toUpperCase, slice, substring, replace, replaceAll, padStart, padEnd, default, truncate. Example: {{ toUpperCase(source.field) }}, {{ default(source.field, "fallback") }}.
  • Bare {{ source }} (no field path) does not print the object; only strings, numbers, and booleans resolve to visible text.
  • List datasources in a List block: {{ my_list.markdownLinks "/path/{id}" }} in the items field, each row becomes a markdown link, one per line.
  • List datasources in an array field: {{ my_list.items }} maps rows directly; reference per-item values inside the item template with {{ item.name }}, {{ item.id }}, etc.

What this means for the rest of this documentation

  • SEO’s template-token support (per-record titles/descriptions on a dynamic route) is one application of this same system, not a separate feature.
  • Components Reference → Data (Card Grid, Data List) are Puck components that consume this binding syntax; this page is the mechanism underneath them.
  • Content Relationships needs to be read alongside this page: the “list everything of type X” half of that gap is actually solved natively. The “editor picks a specific curated subset” half is not, and that’s the part still worth scoping as real work.

Open questions for this page now live on Outstanding Questions, tracked centrally across all pages rather than repeated per page.

Last updated on