Parameter binding
Every SQL query and HTTP endpoint receives its values through a consistent parameter model: the connector builder declares the parameters; the page that consumes the connector (a screen, a chart, a dashboard) surfaces them as form inputs; the framework resolves each input through a small chain of fall-backs before running the query.
This page covers how parameters appear in the UI on each surface, where defaults come from, how cascading filters narrow a dropdown based on another, and the special session context every query gets for free.
At a glance
Declaring a parameter
In Settings → Connectors, open a connector and switch to the Parameters tab. The tab is a table — one row per parameter — with the editor fields below.
| Editor field | Description |
|---|---|
| Name | The parameter identifier. Surfaces as the placeholder name in the query (:name) and as the form-input label fallback. |
| Type | string / int / float / bool / date / datetime / decimal. Drives the widget type on screens (text input vs date picker vs checkbox) and the coercion before the query runs. |
| Label | Display label of the input on the consuming page. Falls back to Name when empty. Localised through the dictionary. |
| Required | When on, the consuming page must provide a value or the request fails with 400 Bad Request. With Required off, Default fills in when omitted. |
| Default | Value applied when the caller omits the parameter. See Defaults. |
| Lookup | Dropdown of dictionary lookups. When set, the form-input becomes a dropdown of { value, label } pairs from the lookup. |
| Multiple | When on, the input accepts a list; the query receives an IN (...) clause. |
| Filter from | Multi-select of other parameters this one cascades from — see Cascading filters. |
Parameters that appear in the query (:name) but aren't declared still bind from the caller — they just have no UI widget and no default. Useful when the value is always set by another mechanism (a chart's fixed value, a job's params).
The connector builder's Parameters tab validates each row in real time — an invalid combination (e.g. Required on without a Default and no Lookup) is flagged before save.
Where parameters surface
A parameter shows up on every page that consumes the connector:
Screens
A screen's toolbar surfaces one input per declared parameter. The widget depends on Type and Lookup:
| Type / lookup | Widget |
|---|---|
string | Text input |
int / float / decimal | Numeric input |
bool | Checkbox |
date / datetime | Date / datetime picker |
| Any type with Lookup set | Dropdown populated from the lookup |
string + Multiple on | Multi-select |
The toolbar's Apply / Run button re-runs the read query with the current values. The framework debounces text inputs (~300 ms) so typing doesn't hammer the database.
Charts
Each chart entry has a Fixed parameters panel in its builder. Operators set the value once; the chart always runs against those values. Useful for "Q1 only" or "billing app only" charts. The ${month.first} style tokens are accepted here so the chart tracks the calendar.
Dashboards
Dashboards add a step: the dashboard's Shared filter bar (top of the layout) can expose a parameter once, and every chart referencing the same parameter name inherits the value. Operators see one filter at the top of the dashboard, not one per panel.
Jobs
A job step's Parameters table is the same shape as the connector's declaration — one row per parameter, name read-only, value editable. Substitutions like ${params.period} chain across steps.
Defaults
Three forms of Default are accepted on the connector builder:
| Form | Resolves to |
|---|---|
Literal — open, 5, true | The literal value. |
Date token — ${today}, ${yesterday}, ${week.monday}, ${week.sunday}, ${month.first}, ${month.last}, ${month.previous} | The matching date in the server's timezone, re-evaluated at every call. So the default tracks the calendar. |
Session value — ${session.user}, ${session.lang}, ${session.roles} | The calling user's identity / language / roles. Documented below. |
A Required parameter without a Default and without a caller value causes the call to fail with 400 Bad Request: missing required parameter.
Session context
Three values are always available to every query, even when not declared:
| Variable | Source |
|---|---|
session.user | The sub claim of the JWT — usually the local username or the OIDC email. |
session.lang | The active language (X-Liberty-Lang header, falling back to the user's preference). |
session.roles | A list of the caller's roles. |
These are useful for row-level filters that should never come from the user. In the connector builder's Query field, write the query as:
SELECT * FROM contracts WHERE owner = :session_user;
The framework rewrites session.user to the placeholder :session_user at parse time (SQLAlchemy doesn't accept dots in placeholder names).
Cascading filters
A common pattern: a screen has a Company dropdown and a Contract dropdown — the contracts must narrow based on the company. In the connector builder's Parameters tab, set the Contract parameter's Filter from field to company:
| Setup |
|---|
Both parameters set Lookup — company lookup pulls companies, contract lookup pulls contracts. |
Contract sets Filter from = [company]. |
The lookup query for Contract references :company in its WHERE clause (typically with IS NULL OR to handle the "no company picked" case). |
When the operator picks a company, the framework clears the Contract selection and re-fetches the dropdown with the new company. Multiple dependencies are supported — Filter from = [company, region].
The cascade is set up entirely from the dictionary (lookup definitions) and the connector builder; no SQL is written from the consuming screen.
Multiple values
A parameter with Multiple on accepts a list of values and binds it as IN (:name) in the query. The form widget becomes a multi-select.
In the query field, write WHERE status IN (:statuses) — the framework expands the placeholder behind the scenes; the literal IN (:statuses) is what the operator types. An empty list is rejected (the SQL IN () is illegal in most databases); pair Multiple with Required: off + a sensible default to handle the empty case.
How it looks at runtime
For a screen of the tasks connector with no toolbar value set, the framework builds the resolved parameter map at request time:
status = "open" (default)
from_date = 2026-05-01 (date token expanded)
to_date = 2026-05-31 (date token expanded)
session.user = "alice" (session)
session.lang = "en" (session)
session.roles = ["viewer", "editor"] (session)
…and runs the query with it. The browser dev-tools network panel shows the request; the framework's debug logger (with LIBERTY_LOG_LEVEL=DEBUG) prints the resolved parameter map next to the SQL.
Permissions
A query inherits the connector's permission code (sql:<connector>:<query>, api:<connector>:<endpoint>). The framework refuses any call to a query the caller can't run; the consuming page (screen / chart / dashboard) also prunes the surface so the operator doesn't see an unusable widget. See Roles & permissions.
Under the hood
Parameter declarations live on the connector entry inside connectors.toml. Operators do not edit this file by hand; the connector builder is the canonical interface. Advanced operators can reach for the Raw TOML tab as an escape hatch when a builder gap blocks them.
What's next
- Concepts → Connectors — the connector definition that holds the queries.
- Concepts → Dictionary — the lookup definitions referenced by the Lookup field.
- Concepts → Form conditions — conditional
visible_when/required_whenrules on screens.