postgrest icon indicating copy to clipboard operation
postgrest copied to clipboard

Use `Prefer: schema=x` as an alternative to Accept-Profile/Content-Profile

Open steve-chavez opened this issue 1 year ago • 6 comments

Problem

Changing from Accept-Profile to Content-Profile depending on the HTTP request method can be confusing. See https://github.com/PostgREST/postgrest/issues/2890#issuecomment-1665169758 and https://github.com/PostgREST/postgrest/issues/3231.

We don't really need the flexibility of saying insert into into schema.users but give me the results of another_schema.users on the same request. This can be achieved in theory with Content-Profile from Accept-Profile.

Solution

Use Prefer: schema=x instead and apply it to all HTTP methods.

steve-chavez avatar Sep 05 '23 15:09 steve-chavez

This would be great, and help clean up some Caddy Config code I've had to implement in order to abstract and "fix" this.

I've seen many end users struggle with this too and make the common mistake of using the wrong header.

robertsosinski avatar Sep 13 '23 13:09 robertsosinski

If we really want to make working with multiple schemas easy, we should reconsider just doing that via the request's path, like /schema1/tableA, /schema2/viewB etc.

wolfgangwalther avatar Sep 15 '23 12:09 wolfgangwalther

like /schema1/tableA, /schema2/viewB

That should be in its own issue but I'd be against it because:

  • it makes the url brittle (a schema is an implementation detail and a rename would break the url)
  • would make impossible to do resource embedding across different schemas (if we ever want to)
  • will make custom urls harder to implement later on (https://github.com/PostgREST/postgrest/issues/1086#issuecomment-1513910590)

steve-chavez avatar Sep 15 '23 16:09 steve-chavez

it makes the url brittle (a schema is an implementation detail and a rename would break the url)

That's not true. A schema is not an implementation detail as soon as you use multiple of them. Right now, you need to pass the schema in the -Profile headers. That makes it part of the API.

Whether to change a header or the path in the URL if you change the schema name - does not make a difference. You need to change it anyway.

would make impossible to do resource embedding across different schemas (if we ever want to)

Not true, either. You just need syntax. you could do /schema1/table1?select=*,schema2/table2(*), for example. That's actually quite natural and much easier to deal with name conflicts compared to the header approach.

will make custom urls harder to implement later on

And of course I don't agree here as well. If done right, this could actually be a step towards custom URLs.

wolfgangwalther avatar Sep 18 '23 07:09 wolfgangwalther

@wolfgangwalther I've opened a new issue for the schema path idea: https://github.com/PostgREST/postgrest/issues/2956

I guess we can have both? I don't see why one would exclude the other. This issue is mostly about improving the current header way.

steve-chavez avatar Sep 18 '23 14:09 steve-chavez

Now that we have Prefer: handling=strict, we can use Prefer: handling=strict; schema=xx instead. No need for creating a new header.

steve-chavez avatar Feb 19 '24 15:02 steve-chavez

Prefer is not the solution to everything. Especially not this. When you do GET /xyz with a different schema... it's an entirely different endpoint / resource. This is not what Prefer is about. I am very much :-1: on this.

wolfgangwalther avatar Mar 30 '24 20:03 wolfgangwalther

Especially not this. When you do GET /xyz with a different schema... it's an entirely different endpoint / resource

That doesn't look right according to REST definition of resource.

https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

A resource is a conceptual mapping to a set of entities This abstract definition of a resource enables key features of the Web architecture. First, it provides generality by encompassing many sources of information without artificially distinguishing them by type or implementation Finally, it allows an author to reference the concept rather than some singular representation of that concept, thus removing the need to change all existing links whenever the representation changes

A schema is an implementation detail. The "concept" (say "authors") can move schemas and it'll still be the same. Having the schema on the path results in brittle URLs.

This way will also give us the flexibility of doing cross-schema resource embedding if the need arises. Using paths for schemas will overcomplicate the syntax /schema1/projects?select=schema2/projects?


Besides that Prefer is just an improvement over the existing Accept/Content-Profile headers so not a new feature. I don't see why not improve things.

Prefer is not the solution to everything.

It's the only header we can use without inventing an X-Something, which is much more likely to give trouble. If you have an alternative header, I'm all ears..

steve-chavez avatar Apr 02 '24 01:04 steve-chavez

Especially not this. When you do GET /xyz with a different schema... it's an entirely different endpoint / resource

That doesn't look right according to REST definition of resource.

How so? I think we are just talking about two different things.

Your scenario is more like versioning for example. Assume a v1 and a v2 schema. Both have a view authors pointing to the same table. The v2.authors view exposes an additional column.

I fully agree with you - this is not a different "thing".

But this is also just a special case. Imagine two schemas, with two tables, both named items. Entirely different tables with no connection whatsoever. Those are two different "things" - and you shouldn't need to make a difference via header. That's just not correct. The only sane way to map this to http is to have two different endpoints.

A schema is an implementation detail. The "concept" (say "authors") can move schemas and it'll still be the same. Having the schema on the path results in brittle URLs.

In the versioning example it might be (although the api version is hardly an implementation detail, but the fact that this is represented as a schema is). In the other example, it might very well not be an implementation detail. It might give you a much required context to actually be able to tell what "kind of items" you are dealing with.

This brittle URL thing.. I really don't get it. If you use schemas in the URL - of course the URL will change when you change the schema. But the URL will also change when you rename the table or view. Why is that not an implementation detail? Where do you draw the line?

This way will also give us the flexibility of doing cross-schema resource embedding if the need arises. Using paths for schemas will overcomplicate the syntax /schema1/projects?select=schema2/projects?

Uhm, how exactly do you put the fact that two tables in the embedding spec are in different schemas in the header? I don't get it. This seems much more complicated - because you suddenly need to draw a connection between the header and the select= embedding. The url-only syntax you proposed... is really simple and nice!

Besides that Prefer is just an improvement over the existing Accept/Content-Profile headers so not a new feature. I don't see why not improve things.

Well, the Accept/Content-Profile headers turned out to be... not a good idea, imho. And not only because of the different header names for GET and POST - but in general.

wolfgangwalther avatar Apr 02 '24 19:04 wolfgangwalther

But this is also just a special case. Imagine two schemas, with two tables, both named items. Entirely different tables with no connection whatsoever. Those are two different "things" - and you shouldn't need to make a difference via header. That's just not correct. The only sane way to map this to http is to have two different endpoints.

From a conceptual point of view (the REST one), those items you refer to should be different concepts. The correct db design for that is to have only one parent table items then use table inheritance for childs items_x and items_y. Those would be different endpoints then.

But the URL will also change when you rename the table or view. Why is that not an implementation detail? Where do you draw the line?

IMO at the relation level. Databases and schemas are boxes of relations. If you rename the table/view then it is a different relation thus a different concept.

Well, the Accept/Content-Profile headers turned out to be... not a good idea, imho. And not only because of the different header names for GET and POST - but in general.

Hm, I don't see why. IME besides the different headers that has been working just fine. Simple in interface, implementation and maintenance.


We had another discussion about the Host header pointing to different databases. Following your URL argument here, should we now use /db/schema/table? (Like prest does?) Typing that.. looks inconvenient for users.

Maybe that's the real win we have with the header approach. We're able to provide default schemas/databases for our endpoints and keep them simpler.

steve-chavez avatar Apr 02 '24 23:04 steve-chavez

From a conceptual point of view (the REST one), those items you refer to should be different concepts. The correct db design for that is to have only one parent table items then use table inheritance for childs items_x and items_y. Those would be different endpoints then.

I get the idea, although I don't think this would be respected in practice. Name collisions could also happen in homographs too.


Since I was working on bulding the OpenAPI spec, I noticed that it's quite difficult to work with the Accept-Profile header, since the response, filters and permissions may change when that header is present and the endpoint is the same. Separating it into a different endpoint instead of a header helps a lot in simplifying this. Of course, we shouldn't build our API around OpenAPI, but I think it's good to know that the issue is there.

laurenceisla avatar Apr 03 '24 01:04 laurenceisla

OpenAPI is a valid point. So in general is harder to provide a static spec with the header approach.

In that case I guess we should also ditch the Host idea for mapping an endpoint to another database relation.

steve-chavez avatar Apr 03 '24 01:04 steve-chavez

Let's move forward with the schema in URL then. Will open a new issue for that.

steve-chavez avatar Apr 03 '24 04:04 steve-chavez

We had another discussion about the Host header pointing to different databases. Following your URL argument here, should we now use /db/schema/table? (Like prest does?) Typing that.. looks inconvenient for users.

The fact that the host is transferred as a header is mostly an implementation detail of http - because nobody uses it like that. Everybody uses it as part of the URL, so there is no reason to change my proposal.

wolfgangwalther avatar Apr 03 '24 06:04 wolfgangwalther

Let's move forward with the schema in URL then. Will open a new issue for that.

We already have #2157.

wolfgangwalther avatar Apr 03 '24 06:04 wolfgangwalther