From cc480915c879fba517afa80313d8a799f5625ca8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 16:32:51 +0000 Subject: [PATCH] docs(rest): record the #8039 ruling on DATA_RECORD_READ_PARAMS The docblock above DATA_RECORD_READ_PARAMS (GET /data/:object/:id's closed query-parameter set) described the fields/populate alias-table spellings as an open question tracked by #8039. Maintainer ruling, 2026-08-12, settled it: option 2 - keep the narrow select/expand set, refuse the alias spellings loudly instead of folding them. Option 1 (folding RPC_QUERY_ALIAS_SLOTS onto this one route) was explicitly rejected as surface expansion with no measured pull, and would leave other data routes' ingress inconsistent in the opposite direction. No code change. refuseUnknownQueryParams(req, res, DATA_RECORD_READ_PARAMS) already refuses `?fields=` / `?populate=` with a located 400 VALIDATION_ERROR - measured directly against origin/main before this commit by running rest-server-closed-query-params.test.ts (27/27 passing), including the two cases that drive both spellings through the real by-id handler and assert the refusal plus that getData is never called. The behavioural half of the ruling was already shipped; this commit is the prose half the ruling asked for. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01P7vaLs7bhBPi9m3JyzkhDj --- .../data-record-read-alias-ruling-comment.md | 28 +++++++++++++ packages/rest/src/rest-server.ts | 41 ++++++++++++++----- 2 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 .changeset/data-record-read-alias-ruling-comment.md diff --git a/.changeset/data-record-read-alias-ruling-comment.md b/.changeset/data-record-read-alias-ruling-comment.md new file mode 100644 index 0000000000..fd6859ecd4 --- /dev/null +++ b/.changeset/data-record-read-alias-ruling-comment.md @@ -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`. diff --git a/packages/rest/src/rest-server.ts b/packages/rest/src/rest-server.ts index 0964636b0f..9c59a03e4f 100644 --- a/packages/rest/src/rest-server.ts +++ b/packages/rest/src/rest-server.ts @@ -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'];