ADR 004: Vanilla Kiota request-configuration pattern for query parameters
Status
Accepted
Context
Every API module exposes a *QueryParameters struct per verb (e.g.
TableRequestBuilderGetQueryParameters) that gets applied to the outgoing
request. Historically these used plain value fields (Limit int,
Query string, DisplayValue DisplayValue) and were encoded onto the
request via a repo-specific internal.ConfigureRequestInformation /
internal.KiotaRequestInformation wrapper built on the go-querystring
library.
This diverged from how Kiota-generated SDKs (for example, msgraph-sdk-go) shape query
parameters, and the divergence had a real cost: value fields can't represent
"the caller didn't set this" separately from "the caller explicitly set the
zero value," so go-querystring either always emitted a field or needed
extra omitempty-style conventions layered on top to approximate what
abstractions.RequestInformation.AddQueryParameters gives you out of the box with
nil-checkable pointers.
Alternatives considered:
- Keep the value-typed structs and the go-querystring wrapper — rejected
because it's the thing ADR-003 already argues against: reinventing a
Kiota runtime capability (
AddQueryParameters) that ships by default, and it keeps the SDK's query-parameter shape looking different from every Kiota-generated SDK a caller might already know. - Pointer fields wired through
abstractions.ConfigureRequestInformationdirectly — matches msgraph-sdk-go's convention exactly and removes a dependency.
A timeboxed spike (#494) was run specifically to classify this as breaking vs. additive before the v2.0 cut, since it couldn't land after the tag without becoming a v3 change.
Decision
Every exported *QueryParameters struct across every API module uses pointer
fields with uriparametername tags (Limit *int32, Query *string,
DisplayValue *DisplayValue, etc.), and request builders call
abstractions.ConfigureRequestInformation directly instead of a bespoke
wrapper. The internal.ConfigureRequestInformation / internal.KiotaRequestInformation
wrapper and the go-querystring dependency are removed entirely (#511).
A non-obvious constraint this decision carries: the native encoder only
recognizes *int32 explicitly — a bare *int (even as a pointer) is
silently dropped from the request. Any new integer query parameter must be
declared as *int32, not *int, or it will vanish without error.
Consequences
- Pros: query-parameter structs now look and behave like every other
Kiota-generated SDK's
*QueryParameters; "unset" and "zero" are distinguishable the same way ADR-002 already made models' properties distinguishable; one fewer third-party dependency. - Cons: breaking for every caller constructing these structs with value
literals — they must switch to pointers (see
internal.ToPointer). Landed as a breaking v2.0 change specifically because it couldn't wait for v3. - Sharp edge to guard in review: a new
*intquery-parameter field is a silent bug, not a compile error — it must be*int32.