← stitchkitDocumentation

ADR 0073 — Client request options are not callback context

Context

A generated endpoint is intentionally usable as a direct react-query-kit or TanStack Query callback. Those libraries invoke mutation and query functions with contract variables first and their own execution context second. Giving a generated endpoint an optional second ClientRequestOptions parameter makes the function types incompatible and creates a runtime ambiguity when a foreign context happens to contain a similarly named field.

Per-call cancellation still belongs on every generated client surface, but it is transport metadata rather than part of an endpoint’s contract variables or a query library’s callback context.

Decision

Every generated endpoint is a callable object with two explicit surfaces:

  • the ordinary call contains only contract arguments, or no arguments when the contract has none;
  • .withOptions(...) contains the same contract arguments plus a required ClientRequestOptions object.

Both surfaces delegate to one internal (requestArgs, options) executor. The ordinary callable supplies no options and therefore never inspects extra JavaScript arguments provided by a callback host. Only .withOptions validates and consumes transport options.

The previous positional options form is removed. There is no alias or overload because retaining it would preserve the same type collision.

Alternatives rejected

  • Consumer lambdas around every client method: repetitive adaptation that hides a framework-owned composition regression.
  • Accept both positional options and .withOptions: keeps the incompatible call signature and makes runtime ownership of the second argument ambiguous.
  • Detect request options by object shape: a foreign callback context may grow a signal field, so property probing cannot establish ownership.
  • Put cancellation inside contract variables: transport metadata would leak into request schemas, query strings and request bodies.

Consequences

  • Direct mutationFn: api.create and fetcher: api.search composition remains type-safe and runtime-safe.
  • Imperative cancellation is slightly more explicit and mechanically migratable to .withOptions.
  • Plain, batch, scoped and scope-routed clients share one callable shape and one request executor.