Skip to content

Building reports

Parameters, variables & queries

The three separate data concepts in a Golden Reports report and the rules that bind them - including the man-in-the-middle rule.

A report has three data concepts: parameters, query arguments, and variables. They are not the same thing, and the editor enforces their separation. This is the single most important rule to learn before you build anything non-trivial.

Every report has exactly one GraphQL query that runs against its data context (which is itself a GraphQL schema). That query is the report's single fetch; everything the template renders is either a result of that query or a variable.


A short glossary

  • Parameters are the report's external contract - its public API. They're how the outside world feeds values into the report. A user can supply them via a popup before running the report, or a calling system can pass them programmatically in an embed URL or an export request.
  • Query arguments are the operation variables of the report's GraphQL query (e.g. query($id: ID!) { customer(id: $id) { ... } }). Each argument is either a literal or a reference to a parameter.
  • Variables are named values available inside the Liquid template. The template ({{ order.total | money }}) can reference the query result and variables - that's it.
ConceptDefined whereRead by
ParameterData panel → Parameters tabNothing directly. Mapped into the query or into variables.
Query argumentData panel → Query tabThe GraphQL query the platform runs against the data context.
VariableData panel → Variables tabLiquid expressions inside the template.

The man-in-the-middle rule

A parameter cannot appear directly in a Liquid template. To make $currency show up in a footer:

  1. Define a parameter currency (type String, default USD).
  2. Define a variable currency = $currency in the Variables panel.
  3. Reference {{ currency }} in the footer's text block.

Why the rule exists: it keeps the binding graph traceable. If parameters could appear anywhere, refactoring a parameter would mean grepping the entire template; with the rule, you know that every consumer of a parameter is registered as either a query argument or a variable. The editor can also typecheck that path - a parameter typed as Date mapped to a variable expecting a String is caught at design time.

If you only remember one thing

Parameters feed query arguments and variables. The template reads the query result and variables. The template never reads parameters directly.


A worked example

Let's say you're building an invoice report. The caller will pass the invoice ID and the customer's preferred currency.

Parameters

NameTypeDefault
invoiceIdID-
currencyStringUSD

Query

One GraphQL query against the Sales data context, fetching the invoice and its lines, with a single argument: $id:

query InvoiceData($id: ID!) {
  invoice(id: $id) {
    number
    date
    total
    lines {
      description
      quantity
      unitPrice
      subtotal
    }
  }
}

In the Query tab, you bind that $id argument to the parameter $invoiceId. When the report runs, the parameter's runtime value is substituted in before the query executes.

Variables

NameValueNotes
currency$currencyJust exposes the parameter to templates.
todayLiteral nowToday's date, computed at render time.
vipLine{% if invoice.total > 10000 %}VIP customer{% endif %}Computed string.

Templates

In the table column for "Subtotal" you write:

{{ line.subtotal | money: currency }}

line.subtotal is from the query result; currency is a variable that originated as a parameter. Neither $currency nor $invoiceId appear directly in the template.


Why the separation pays off

  • Embedding. A consumer embedding the report only needs to know the parameter names - invoiceId and currency. They never see internal query argument names or variable identifiers, so you can rename things freely.
  • Typing. Each parameter has a declared type. Bindings to query arguments and variables are typechecked at design time; type-incompatible bindings are highlighted with a red chip in the editor.
  • Reusability. Variables that compute values (like vipLine above) can be referenced from many blocks. Variables are the right place for "computed once, used many times" logic - not inline Liquid spread across 30 text blocks.
  • Programmatic access. When the report is run through the API, the request only specifies parameter values. The platform handles propagating them through the query and variables. This is what makes embedding stable.

Parameter types

Parameters are typed. The supported primitives are:

TypeExample
String"USD"
Int42
Float3.14
Booleantrue
Date2026-05-13
DateTime2026-05-13T10:30:00Z
ID"inv_01HABCXYZ"

A parameter can be optional (with a default) or required. Required parameters without a runtime value cause the report to fail validation before any query runs.

Complex inputs (lists, structured objects) are typically modelled as multiple parameters; the platform doesn't currently support nested object parameters at the report boundary.


How values flow at render time

The data layer sees only query arguments. The template sees only query results and variables. The parameter layer is the boundary the outside world interacts with.


Next

Previous
Blocks reference