Typed parameters — AQL
Typed parameters
Section titled “Typed parameters”By default a parameter’s type is inferred from the property it’s compared against (.email = $email → str) or the column it’s assigned to. Params with no such anchor — most commonly limit / offset — have no inferable type and would otherwise generate a loose any field.
Top-level var declarations
Section titled “Top-level var declarations”You can declare and type parameters at the top of your query using a var (...) block or individual var statements:
var ( $status<TransactionStatus>?; $limit<int32>?; $offset<int32>?;)
multi select Transaction { id }filter .status = $statusorder by .created_at desclimit $limitoffset $offset;Or as single var statements:
var $status<TransactionStatus>?;var $limit<int32>?;
multi select Transaction { id } filter .status = $status limit $limit;When declared with var, parameters can be referenced as bare names ($status, $limit) throughout filters, subqueries, and clauses without needing inline <type> annotations repeated at every use site.
:type and <type> are the same
Section titled “:type and <type> are the same”A declaration may spell its type either way. Pick one and stay consistent within a query:
var ( $status: TransactionStatus?; # same as $status<TransactionStatus>? $limit<int32> := 20; # same as $limit: int32 := 20)The optional ? goes after the type, and a := default after that.
Array parameters (multi)
Section titled “Array parameters (multi)”Prefix a declaration with multi to bind a single array value rather than one element:
var ( multi $ids: uuid; multi $roles: UserType;)
multi select User { id, email }filter .id in $ids and .role in $roles;-- $1: ids (uuid)-- $2: roles (str)SELECT u.id AS id, u.email AS emailFROM "user" uWHERE u.id = ANY($1::UUID[]) AND u.role = ANY($2::TEXT[]);Membership against an array parameter lowers to = ANY($n::T[]), never IN — Postgres IN expects
a parenthesised list and rejects an array bind.
The parameter reaches the generated clients as an array type — one argument, not a spread:
await queries.usersByIds({ ids: ["…", "…"], roles: [UserType.Admin] });rows, err := queries.UsersByIds(ctx, gen.UsersByIdsParams{ Ids: []string{"…", "…"}, Roles: []gen.UserType{gen.UserTypeAdmin},})If the declaration omits the type, the element type is inferred from the column the parameter is compared against:
var ( multi $ages; )multi select User { id } filter .age in $ages; # $ages inferred int32, → u.age = ANY($1)An array parameter may also carry a default, which is what bulk inserts iterate over:
var multi $conditions: str? := {'Hot', 'Cold', 'Fragile'};Array parameters are distinct from multi fields in the schema, though they interact naturally:
a multi scalar column is also compared with = ANY. See
Multi properties.
Inline annotations
Section titled “Inline annotations”Alternatively, annotate a parameter inline where it is used with $name<type>. The annotation goes before any ?:
multi select Transaction { id }filter .status = $status<TransactionStatus>order by .created_at desclimit $limit<int32>?offset $offset<int32>?;The type may name any declared value type from your schema:
- a builtin scalar —
str,int16/int32/int64,float32/float64,bool,uuid,datetime,date,time,json,bytes,decimal - a scalar alias — e.g.
scalar type EmailStr extends strrenders as its base builtin - an enum — e.g.
TransactionStatus, which generates the real enum type in code (GoTransactionStatus, TypeScriptTransactionStatus) rather than a barestring
Object types (tables) are not valid parameter types — a parameter is a value, not a row — and an unknown type name is a compile error.
Annotations override inference, so an explicit annotation always wins. Even without one, an enum-backed column is inferred as its enum type: filter .status = $status types $status as TransactionStatus automatically.