You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(data): a dotted-path sort is refused, not silently unapplied (#4256) (#4303)
The one sort shape #4226 deliberately left open. `?sort=account.company_name`
passed the gate on its head segment (a real field) and was then unusable by
every driver: SqlDriver rendered `"account"."company_name"` against a table
that was never joined and the #3821 backstop retried WITHOUT the sort; Mongo
and the memory driver resolved the path against the row, where a foreign key
is a scalar id. 200, every row present, arbitrary order — which `top` then
sliced into an arbitrary "latest N".
Now `400 INVALID_SORT` at the shared normalizer, with the message split by
what the head segment is: a relationship head names the relation it tried to
cross and prescribes denormalising the value onto the queried object (formula
/ rollup field); a non-reference head states the contract (sort reaches whole
columns, not values inside them). Unknown heads keep the #4226 typo answer,
reported first — the same precedence the expand gate uses.
A survey of framework, objectui and cloud found zero callers emitting a
dotted sort; objectui's column-header sort structurally cannot produce one
(lookup columns are keyed by their flat field name, relations load via
$expand). Blast radius is hand-authored requests — exactly the callers the
silent degradation was misleading.
Closes#4256
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: content/docs/api/data-api.mdx
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,7 +20,7 @@ Query records with filtering, sorting, selection, and pagination.
20
20
|`object`| path | Object name |
21
21
|`select`| query | Comma-separated field names. Every name must exist — an unknown one is `400 INVALID_FIELD`, never dropped. |
22
22
|`filter`| query | Filter expression (JSON). `filters` also accepted for backward compatibility. Malformed JSON is rejected with `400 INVALID_FILTER` — never ignored. |
23
-
|`sort`| query | Sort expression (e.g. `name asc` or `-created_at`). Must name a real field — otherwise`400 INVALID_SORT`. |
23
+
|`sort`| query | Sort expression (e.g. `name asc` or `-created_at`). Must name a real field on the object itself — an unknown name or a dotted path (`account.company_name`) is`400 INVALID_SORT`. |
24
24
|`top`| query | Max records to return. No default — omitting it returns all matching records. |
25
25
|`skip`| query | Offset |
26
26
|`expand`| query | Comma-separated list of relations to eager-load. Must name a reference field (`lookup` / `master_detail` / `user` / `tree`) — otherwise `400 INVALID_FIELD`. |
@@ -88,6 +88,7 @@ wrong — three more responses that looked exactly like successful ones:
88
88
|:---|:---|
89
89
|`?sort=-created_at`| sorts |
90
90
|`?sort=no_such_field`|`400 INVALID_SORT`|
91
+
|`?sort=account.company_name`|`400 INVALID_SORT` — sort reaches only the object's own columns; denormalise the related value (formula/rollup field) to sort by it |
91
92
|`?sort={oops` / `?sort=title:desc`|`400 INVALID_SORT` — the list route spells a direction with a space (`title desc`) or a leading `-`|
0 commit comments