-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement new SEARCH/QUERY
HTTP method to pass arguments in request body for read request
#2125
Comments
See PostgREST/postgrest-docs#417.
|
QUERY
method to pass arguments in request body for read request
Even the new QUERY http method won't help with cases where Perhaps we should have a POST with a special body + content type(similar to #465) for these cases? Come to think of it, QUERY would help because it will contain the DELETE/UPDATE inside. |
Makes sense.
Why Not sure whether it could be used for
I don't understand what you're saying here. |
The example here:
Hints that we could have a |
You mean we would do an HTTP DELETE with a body? I thought that was frowned upon according to this. Perhaps some proxies don't even pass the body in a DELETE, we'd have to test that. |
I did a quick search for it, but only found other posts that said it was fine. Your link has a much more authoritative source, though... So So what goes in the body for that This would even work with http semantics somehow: We are creating a short-lived new resource of type "query". Since a query is not part of any of the exposed URIs, but rather part of the whole api, it makes sense to put it at the root endpoint.
Having that in the body would not be a problem at all, I think. But I understand the
"safe" is defined as:
|
Not adamantly opposed, but that solution feels like "chucking it all".
The problem really is the So perhaps we could do something like: POST /projects?status=eq.active
Content-Type: application/vnd.pgrst.big-in-filter+json
{
"method": "DELETE"
, "in": [1,2,3,4,5,6]
} That way we get to reuse path, headers and querystring. |
Hm. For such a specific solution that only targets a single filter, you can easily create an RPC - that should be good enough already. |
Yeah, but we kinda miss the "low-code" advantage with that. I thought we could have the above interface so client libraries can abstract the |
A multipart type could be an option to the above. |
I can see other use-cases, too: Passing a value in a JSON body should allow to work around some of the quoting/escaping issues/challenges we have in the query string.
Only supporting that special cases just seems so... arbitrary.
I can see how that's a positive. What about: POST /projects?status=eq.active&id=in.refs->my_ids
Content-Type: application/vnd.pgrst.params-in-body+json
{
"method": "DELETE"
, "body": "... original body ..."
, "refs": {
"my_ids": [1,2,3,4,5,6]
}
} (Note: The "original body" was just meant for The idea would be to somehow allow referencing values in the body in the query string. This way they could be re-used for all kinds of filters, not only for Some ideas for syntax:
Those would not be breaking changes, because they would only be interpreted if the special |
Nice! I like that direction. There's another concern I have. This type of bodies will definitely be big and parsing them in Haskell would lead us to have the same problem as #690. So ideally we want to send the body directly to postgres and avoid processing it. With the
I grow dissatisfied with answers with just authoritative/purist points of view and it has come to my attention that Elasticsearch allows doing a GET with a request body. How about if we support for GET/DELETE with a request body for these exceptional cases? Seems javascript References:
Edit: Passing the method in the header would be another option. |
Fully agree.
Do we need to support I think I'd be fine with that. For completeness, there is also a different way, which would be the most "REST-like", I guess: When passing those parameters in the body, we break the "the same URI describes the same resource" promise - because with a different body, a completely different resource is targeted. This can be avoided by creating temporary resources in two http requests:
The second request would then have the unique identifier in the URI and would not break that promise above anymore. Those temporary resources could be represented as real database objects, e.g. temp tables or functions in the temporary schema. Although I don't have an idea, yet, how that would work with a connection pool. |
Another option could be the REPORT method:
According to this blog post it already works everywhere.
Looks like the REPORT method addresses that? The resource can depend on the request body.
https://www.rfc-editor.org/rfc/rfc3253.html#page-25 Other references:
|
Branched off the DELETE with a body discussion to #2314 |
Thanks guys for the good work so far. Any idea when this feature could be implemented? i am waiting for it, passing arguments in request body when GETing or QUERYing for data |
DELETE with body will definitely come in v10. This one would be for the next version. I think we should go with the SEARCH method as it's older and likely better supported. With that the above proposal would be: SEARCH /projects?status=eq.active&id=in.refs->my_ids
Content-Type: application/json
{
"refs": {
"my_ids": [1,2,3,4,5,6]
}
} Or to avoid an special case in our filter operators we could: SEARCH /projects?status=eq.active&id=_in.my_ids
Content-Type: application/json
{
"my_ids": [1,2,3,4,5,6]
} Where every operator with an underscore(
|
QUERY
method to pass arguments in request body for read requestSEARCH/QUERY
method to pass arguments in request body for read request
SEARCH/QUERY
method to pass arguments in request body for read requestSEARCH/QUERY
HTTP method to pass arguments in request body for read request
The SEARCH method could also allow us to pass parameters that are more bulky in nature, like for PostGIS operators: SEARCH /projects?range_area=_st_within.area
Content-Type: application/json
{
"area": {
"type": "Polygon",
"coordinates": [
[
[
-71.10034391283989,
42.37385299961788
],
[
-71.10036939382553,
42.373756895982865
],
[
-71.1002916097641,
42.373745997623224
],
[
-71.1002641171217,
42.37384408279195
],
[
-71.10034391283989,
42.37385299961788
]
]
]
}
} |
Right now we can only use our operators against other values, we're missing operating against other identifiers. Underscore operators could be used to operate on columns besides referencing values on the request body. This would allow doing column to column comparison(asked before) without computed columns: SEARCH /PredictedGenes?Strand=_eq.+&GeneStart=_gt.GeneEnd Operating on a body value would still be allowed, SEARCH /PredictedGenes?Strand=_eq.body->strand&GeneStart=_lt.GeneEnd
{ "Strand": "-"} This would favor PATCH/DELETE as well.
@wolfgangwalther Just realized that If we implement the set operator for PATCH, we wouldn't need a new Underscore operators is arbitrary, they could also be dollar operators - Also, like set, they could potentially take an expression: SEARCH /users?createdAt=_gt.(body->start::timestamptz,minus,body->lapse)
{"start": "now", "lapse": "3 days"}
SELECT * FROM users WHERE "createdAt" > 'now'::timestamptz - '3 days'; |
Another option for underscore operators is to use a jsonpath expression as the operand. DELETE /tbl?id=_eq.$[*].id
[
{"id": 1},
{"id": 2}
] One good thing is that we'd get to reuse existing pg functionality. Another one that since One difficulty I've noted with this is that we'd need the schema cache to cast the select * from test.projects where id = (('{"a": 3}'::json)->>'a');
ERROR: operator does not exist: integer = text
select * from test.projects where id = (('{"a": 3}'::json)->>'a')::int;
id | name | client_id
----+------+-----------
3 | IOS | 2
(1 row) It doesn't seem possible to obtain the json with an Another drawback would be that json path includes brackets Still seems useful, it might be possible to offer json path depending on a different json path does too much imo, some comments on #1883 (comment). |
I don't really speak |
Yes, otherwise there was a syntax error IIRC. You can also use I no longer think that json path is safe to expose though, made a comment on #1883 (comment) |
I'd love to see this feature. |
Elaborating on the above. Instead of underscore operators, I think dollar operators would be better: SEARCH /PredictedGenes?Strand=$eq.$body.strand&GeneStart=$lt.GeneEnd
{ "Strand": "-"} It looks more differentiable/clearer that it's our own syntax. And instead of having |
What if we use the our query grammar in the media type parameters. Like: SEARCH /PredictedGenes
Content-Type: application/vnd.pgrst.search+json; Strand.eq=$Strand; GeneStart.lt=GeneEnd
{ "Strand": "-"} This would make it easier to implement as the query string grammar is not modified. Media type parameters are also proposed for PATCH #465 (comment). Also, maybe later this could be used for solving client-side transactions(#286). POST / HTTP/1.1
Content-Type: multipart/form-data;boundary="boundary"
--boundary
Content-Type: application/vnd.pgrst.search+json; col.eq=$field;
Content-Disposition: form-data; name="tbl";
{..}
--boundary
Content-Type: application/vnd.pgrst.patch+json; another_col.eq=$another_field;
Content-Disposition: form-data; name="other_tbl";
{...} Maybe we can have another multipart media type for clarity. Like |
On #2310 (comment) Wolfgang, mentions:
Just using SEARCH /projects
Content-Type: application/x-www-form-urlencoded
status=eq.active&id=in.[1,2,3,4,5,6,....] (Not sure how would this look with PATCH though) One problem is that then doing #2816 won't be possible, we'd have to parse the body before sending the query. On the contrary if we had: SEARCH /projects
Content-Type: application/vnd.pgrst.filters+json;status.eq=$status;id.in=$id
{"status": "active", "id": [1,2,3,4,5,6]} Then we wouldn't have to parse the body at all. We could just hash the URL + the header. Although the |
Wondering if we should do this. This recent article: https://bessey.dev/blog/2024/05/24/why-im-over-graphql/#query-parsing Mentions how sending a long list over the POST body can be abused to cause OOM, and how mitigating this is a pain in GraphQL. So for us, since we use the URL for our query language, the URL size limit has always served as a built-in defense mechanism. In a way, this limitation has always been a feature. Perhaps we should document it as such? And try to give a better error message if it happens? |
It seems like this is not specific to POST. It's just a very special syntax, which amplifies memory usage way beyond the payload size. I don't see a problem here for us, yet. Especially because we have not a single parser anywhere, which would continue on an error and collect multiple of them. Everything we have so far throws on the first error immediately. |
Thank you for this wonderful and magical api... i would like to send alot of arguments while doing a GET request.
Does PostgREST as an API have a limit on the url length ? Is there possibility to POST a json object to the GET endpoint?
The text was updated successfully, but these errors were encountered: