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'];