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): reject unknown list query params instead of reading them as zero-matching filters (#4134) (#4169)
`GET /api/v1/data/:object` reads any parameter it does not reserve as a
field-level equality filter. When the name matched no field, the predicate
could only ever match nothing, so `?pageSize=5` returned 200 + `total: 0` —
indistinguishable from an empty object, while the write path already rejected
the same unknown name with `INVALID_FIELD`.
`findData` now validates leftover parameters against the object's field map
before lowering them into implicit filters, and throws the write path's own
envelope: 400 `INVALID_FIELD` + `field` + `object`, with a suggestion (the
canonical parameter for a known dialect, or the closest field name for a typo).
The check runs before the `!options.where` branch, so the rejection does not
depend on whether an explicit `filter` rode along. The reserved set is
`Record<keyof QueryAST, true>`-pinned to the spec.
The known-field-plus-explicit-filter case is left alone and filed as #4164.
fix(data): reject unknown list query parameters instead of reading them as zero-matching field filters (#4134)
7
+
8
+
`GET /api/v1/data/:object` reads any parameter it does not reserve as a
9
+
field-level equality filter — that is what makes `?status=done` shorthand for
10
+
`?filter={"status":"done"}`. When the name matched **no** field the resulting
11
+
predicate could only ever match nothing, so `?pageSize=5` on a 10-row object
12
+
returned `200` + `total: 0`: structurally valid, and indistinguishable from
13
+
"this object is empty". The write path already rejected the same unknown name
14
+
loudly (`400 INVALID_FIELD`), so one piece of knowledge — does this field
15
+
exist — was enforced on write and silently zeroed on read.
16
+
17
+
The read path now answers the same way, in the same envelope:
18
+
19
+
```json
20
+
{
21
+
"error": "Unknown field 'pageSize' on object 'showcase_task'. Query parameters that are not reserved are read as field filters, so an unknown name can only match zero records. Did you mean the 'top' query parameter (OData spelling '$top')?",
22
+
"code": "INVALID_FIELD",
23
+
"field": "pageSize",
24
+
"object": "showcase_task"
25
+
}
26
+
```
27
+
28
+
The rejection carries a suggestion — the canonical parameter for a known
Copy file name to clipboardExpand all lines: content/docs/api/data-api.mdx
+29Lines changed: 29 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,6 +28,35 @@ Query records with filtering, sorting, selection, and pagination.
28
28
29
29
> **Note:** OData-style `$`-prefixed parameters (`$filter`, `$select`, `$orderby`, `$top`, `$skip`, `$expand`, `$count`, `$search`) are also accepted directly on this same endpoint as aliases — they're normalized internally to the parameter names above. There is no separate standalone OData endpoint.
30
30
31
+
#### Any other parameter is a field filter — and must name a real field
32
+
33
+
A query parameter the endpoint does not reserve is read as a field-level
34
+
equality filter, so `?status=done` is shorthand for
35
+
`?filter={"status":"done"}`. Because such a parameter *is* a predicate, one
36
+
naming a field the object does not have could only ever match zero records —
37
+
so the endpoint rejects it instead of returning an empty page:
38
+
39
+
```http
40
+
GET /api/v1/data/showcase_task?pageSize=5
41
+
```
42
+
```json
43
+
{
44
+
"error": "Unknown field 'pageSize' on object 'showcase_task'. Query parameters that are not reserved are read as field filters, so an unknown name can only match zero records. Did you mean the 'top' query parameter (OData spelling '$top')?",
45
+
"code": "INVALID_FIELD",
46
+
"field": "pageSize",
47
+
"object": "showcase_task"
48
+
}
49
+
```
50
+
51
+
This is the same `400 INVALID_FIELD` the write path returns for an unknown
52
+
field name, and it applies whether or not an explicit `filter` rode along.
53
+
Page size is `top` / `$top` / `limit` — `pageSize`, `page_size` and `perPage`
54
+
are not accepted spellings.
55
+
56
+
Reserved names cannot double as implicit filters. An object with a field
57
+
literally called `count`, `cursor`, `distinct`, `object`, `search` or `top`
58
+
filters it through the explicit form (`?filter={"count":3}`).
0 commit comments