Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .changeset/data-record-read-alias-ruling-comment.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
"@objectstack/rest": patch
---

docs(rest): record the #8039 ruling on `GET /data/:object/:id`'s query-parameter set —
`fields` / `populate` are refused BY DESIGN, not an open question (#8039)

Documentation only — `refuseUnknownQueryParams` already rejects any input the same way
it did before this change.

The route accepts exactly `select` / `expand`. It never folds the spec's alias table
(`RPC_QUERY_ALIAS_SLOTS`, which maps `fields` → alias `select` and `expand` → alias
`populate`), so the canonical `fields` spelling and the `populate` alias are outside the
accepted set and refused with a located `400 VALIDATION_ERROR` naming `select` / `expand`
as what the route accepts — never silently dropped.

That gap used to be recorded as an open question ("tracked as #8039 … rather than widened
here"). It is now settled: maintainer ruling, 2026-08-12, took **option 2** — keep the
narrow set, refuse the alias-table spellings loudly. **Option 1 (folding
`RPC_QUERY_ALIAS_SLOTS` onto this one route, so `fields` / `populate` start working here
too) was explicitly rejected** — it would be surface expansion on a public route with no
measured pull behind it, and doing it for this route alone would leave every other data
route's ingress inconsistent in the opposite direction. If the alias table is ever
declared universal across data routes, that lands as one card applying the fold to ALL
data routes at once, with its own ruling — never a quiet per-route widening.

For anyone integrating against this route: `?fields=…` and `?populate=…` are not aliases
here and will not become one without a separate, wider decision. Use `select` / `expand`.
41 changes: 30 additions & 11 deletions packages/rest/src/rest-server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1524,17 +1524,36 @@ export const APPROVAL_REQUEST_LIST_PARAMS: readonly string[] = [
* so every other parameter on this route is dropped in the fullest sense: it
* never reaches `getData` at all.
*
* ⚠️ The dropped names include the CANONICAL spelling of one slot. The spec's
* alias table (`RPC_QUERY_ALIAS_SLOTS`) declares the fields slot as canonical
* `fields` with alias `select`, and the expand slot as canonical `expand` with
* alias `populate` — but this route folds no aliases, so `?fields=name`
* silently returns the FULL record and `?populate=…` silently expands nothing.
* Both are outside this set on purpose: adding them here would advertise a
* capability the handler does not implement, which is the declared-≠-enforced
* trap in the other direction. Refusing them instead makes the gap
* self-reporting — the located message names `select` / `expand` as what this
* route does accept. Tracked as #8039 (an alias-coverage question for the spec
* table and this handler to settle together) rather than widened here.
* ⚠️ The accepted names deliberately EXCLUDE the CANONICAL spelling of one
* slot. The spec's alias table (`RPC_QUERY_ALIAS_SLOTS`) declares the fields
* slot as canonical `fields` with alias `select`, and the expand slot as
* canonical `expand` with alias `populate` — but this route folds no aliases,
* so `fields` / `populate` are not synonyms for anything this handler reads.
* Putting them in the allowlist unfolded would advertise a capability the
* handler does not implement — the declared-≠-enforced trap in the other
* direction, and strictly worse than refusing them: a caller sending
* `?fields=title` would pass recognition, then silently get back the FULL
* record because nothing downstream of the gate consumes the name. Refusing
* them instead makes the gap self-reporting — the located `400` names
* `select` / `expand` as what this route accepts.
*
* **[#8039] Settled by maintainer ruling, 2026-08-12 — record, not an open
* question.** Three shapes were on the table for the mismatch between this
* route's two names and the spec's alias table: (1) fold
* `RPC_QUERY_ALIAS_SLOTS` onto this route, so `fields` / `populate` start
* working here too; (2) keep this narrow set, but refuse the alias-table
* spellings loudly instead of dropping them; (3) keep + document as-is. The
* ruling took **option 2**, which is exactly what `refuseUnknownQueryParams`
* below already does — `fields` and `populate` are refused the same way any
* other unrecognised name is, naming `select` / `expand` as the accepted
* pair. ⛔ **Option 1 was explicitly rejected** and stays rejected here:
* folding the alias table onto this ONE route is surface expansion on a
* public route with no measured pull behind it, and doing it for this route
* alone would leave every other data route's ingress inconsistent in the
* opposite direction. The only thing that changes this: a ruling that
* declares `RPC_QUERY_ALIAS_SLOTS` universal across ALL data routes, landed
* as one card applying the fold everywhere at once — never a quiet widening
* of this route's set in isolation.
*/
export const DATA_RECORD_READ_PARAMS: readonly string[] = ['select', 'expand'];

Expand Down
Loading