-
-
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
OPTIONS no longer works in v0.4.0.0 #790
Comments
Yeah you're right, OPTIONS no longer provides API documentation about a route. However it is still a supported verb and responds with CORS and Allow headers. The API information has been moved to the OpenAPI description available at the root path. Foreign key information is not yet included in the OpenAPI output though. That will have to be a later feature. Are you working on a front-end admin panel or other generic code that needs to learn about the foreign keys between tables? |
Yes, I was working on some sort of admin panel, but eventually I decided to just hard-code the relationships, as it is more appropriate for my use case. I still think it would be better to be able to consult each node for its relationships rather than having to fetch the entire schema from the root path (which can get pretty huge). |
As someone who is not fully familiar with the OpenAPI spec, may I ask: Is the absence of information about the foreign key or embedded resources due to limitations in the OpenAPI specification? Or is it something you have not had an opportunity to implement? In addition to UIs that auto-discover which resources can be embedded, I think this information is really useful to someone learning to use PostgREST. First, appearance of foreign key / embedded resource data during development confirms that the relationship is correct, and that PostgREST picked up on the change (after a HUP). Second, it's obvious that showing the structure makes it easier to explore and use PostgREST. @begriffs In my opinion, I really liked your use of OPTIONS to discover the API. However, I can understand the switch to OpenAPI. I'd love to help reinstate this functionality. Alas, my Haskell foo is too weak. Would you be open to a Haskell noob attempting to assist? |
To be honest I don't know the best way to structure this information in OpenAPI. I have a feeling there's a way to do it though. Are you interested in researching this and providing your recommendation? I agree that it's useful functionality, for instance for building admin panels like in #717. (I'm also happy to help you with any Haskell questions as you begin learning. A good starting place would be to try building the project from source, explained here: https://postgrest.com/en/v0.4/install.html#build-from-source) |
If you're just trying to get a list of the tables available, and perhaps their field names/types, OpenAPI adds a lot of weight :/ for instance, here's a fresh postgres DB with a single table added: // 20170410070412
// http://localhost:3000/
{
"swagger": "2.0",
"info": {
"version": "0.4.0.0",
"title": "PostgREST API",
"description": "This is a dynamic API generated by PostgREST"
},
"host": "0.0.0.0:3000",
"basePath": "/",
"schemes": [
"http"
],
"paths": {
"/": {
"get": {
"tags": [
"/"
],
"produces": [
"application/openapi+json"
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/contacts": {
"get": {
"tags": [
"contacts"
],
"produces": [
"application/json",
"application/vnd.pgrst.object+json",
"text/csv"
],
"parameters": [
{
"required": false,
"in": "header",
"name": "Range",
"type": "string",
"description": "Limiting and Pagination"
},
{
"default": "items",
"required": false,
"in": "header",
"name": "Range-Unit",
"type": "string",
"description": "Limiting and Pagination"
},
{
"required": false,
"in": "query",
"name": "offset",
"type": "string",
"description": "Limiting and Pagination"
},
{
"required": false,
"in": "query",
"name": "limit",
"type": "string",
"description": "Limiting and Pagination"
},
{
"required": false,
"in": "query",
"name": "select",
"type": "string",
"description": "Filtering Columns"
},
{
"required": false,
"in": "query",
"name": "order",
"type": "string",
"enum": [
"id.asc.nullsfirst",
"id.asc.nulllast",
"id.asc",
"id.desc.nullsfirst",
"id.desc.nulllast",
"id.desc",
"id.nullsfirst",
"id.nulllast",
"id",
"first_name.asc.nullsfirst",
"first_name.asc.nulllast",
"first_name.asc",
"first_name.desc.nullsfirst",
"first_name.desc.nulllast",
"first_name.desc",
"first_name.nullsfirst",
"first_name.nulllast",
"first_name",
"last_name.asc.nullsfirst",
"last_name.asc.nulllast",
"last_name.asc",
"last_name.desc.nullsfirst",
"last_name.desc.nulllast",
"last_name.desc",
"last_name.nullsfirst",
"last_name.nulllast",
"last_name",
"email.asc.nullsfirst",
"email.asc.nulllast",
"email.asc",
"email.desc.nullsfirst",
"email.desc.nulllast",
"email.desc",
"email.nullsfirst",
"email.nulllast",
"email",
"gender.asc.nullsfirst",
"gender.asc.nulllast",
"gender.asc",
"gender.desc.nullsfirst",
"gender.desc.nulllast",
"gender.desc",
"gender.nullsfirst",
"gender.nulllast",
"gender",
"ip_address.asc.nullsfirst",
"ip_address.asc.nulllast",
"ip_address.asc",
"ip_address.desc.nullsfirst",
"ip_address.desc.nulllast",
"ip_address.desc",
"ip_address.nullsfirst",
"ip_address.nulllast",
"ip_address"
],
"description": "Ordering"
},
{
"required": false,
"in": "header",
"name": "Prefer",
"type": "string",
"enum": [
"count=none"
],
"description": "Preference"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "integer",
"required": false,
"in": "query",
"name": "id",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "first_name",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "last_name",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "email",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "gender",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "ip_address",
"type": "string"
}
],
"responses": {
"200": {
"description": "OK"
},
"206": {
"description": "Partial Content"
}
}
},
"post": {
"tags": [
"contacts"
],
"consumes": [
"application/json",
"application/vnd.pgrst.object+json",
"text/csv"
],
"produces": [
"application/json",
"application/vnd.pgrst.object+json",
"text/csv"
],
"parameters": [
{
"required": false,
"in": "header",
"name": "Prefer",
"type": "string",
"enum": [
"return=representation",
"return=minimal",
"return=none"
],
"description": "Preference"
},
{
"required": false,
"schema": {
"$ref": "#/definitions/contacts"
},
"in": "body",
"name": "body",
"description": "contacts"
}
],
"responses": {
"200": {
"description": "OK"
},
"201": {
"description": "Created"
}
}
},
"delete": {
"tags": [
"contacts"
],
"produces": [
"application/json",
"application/vnd.pgrst.object+json",
"text/csv"
],
"parameters": [
{
"required": false,
"in": "header",
"name": "Prefer",
"type": "string",
"enum": [
"return=representation",
"return=minimal",
"return=none"
],
"description": "Preference"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "integer",
"required": false,
"in": "query",
"name": "id",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "first_name",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "last_name",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "email",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "gender",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "ip_address",
"type": "string"
}
],
"responses": {
"200": {
"description": "OK"
}
}
},
"patch": {
"tags": [
"contacts"
],
"consumes": [
"application/json",
"application/vnd.pgrst.object+json",
"text/csv"
],
"produces": [
"application/json",
"application/vnd.pgrst.object+json",
"text/csv"
],
"parameters": [
{
"required": false,
"in": "header",
"name": "Prefer",
"type": "string",
"enum": [
"return=representation",
"return=minimal",
"return=none"
],
"description": "Preference"
},
{
"required": false,
"schema": {
"$ref": "#/definitions/contacts"
},
"in": "body",
"name": "body",
"description": "contacts"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "integer",
"required": false,
"in": "query",
"name": "id",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "first_name",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "last_name",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "email",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "gender",
"type": "string"
},
{
"pattern": "^not[.]eq[.]|^not[.]gte[.]|^not[.]gt[.]|^not[.]lte[.]|^not[.]lt[.]|^not[.]neq[.]|^not[.]like[.]|^not[.]ilike[.]|^not[.]in[.]|^not[.]notin[.]|^not[.]isnot[.]|^not[.]is[.]|^not[.]@@[.]|^not[.]@>[.]|^not[.]<@[.]|^eq[.]|^gte[.]|^gt[.]|^lte[.]|^lt[.]|^neq[.]|^like[.]|^ilike[.]|^in[.]|^notin[.]|^isnot[.]|^is[.]|^@@[.]|^@>[.]|^<@[.]",
"format": "character varying",
"required": false,
"in": "query",
"name": "ip_address",
"type": "string"
}
],
"responses": {
"200": {
"description": "OK"
},
"204": {
"description": "No Content"
}
}
}
}
},
"definitions": {
"contacts": {
"properties": {
"id": {
"format": "integer",
"type": "integer"
},
"first_name": {
"format": "character varying",
"type": "string"
},
"last_name": {
"format": "character varying",
"type": "string"
},
"email": {
"format": "character varying",
"type": "string"
},
"gender": {
"format": "character varying",
"type": "string"
},
"ip_address": {
"format": "character varying",
"type": "string"
}
},
"type": "object"
}
}
} |
We could serve a different representation at the root depending on the Accept header. Is there a more concise standard you would prefer? |
During my review of Open API with an eye on adding nested resource details (re. #717 ) I came to a conclusion similar to @timwis. I believe the Open API service description rendered on the root route (I'll resort to calling it the "description" for brevity) is verbose. It's also incomplete. However, a complete description is inevitably untenable. I'm not trying to disparage anyone or start a flame war. I think @begriffs invented something really cool in PostgREST. It's sparked my latent interest in Haskell. And I hope I can rise to the challenge of contributing to this project. Let me explain my opinion and hopefully offer some solutions. Some things seem overly verboseHeader and query parameters (in Problems with the
|
Woah. Some really interesting points there, but a slightly different road than a more concise introspection method. As for an alternate, more concise standard, the only two I know that are relevant are: I think the table schema probably makes more sense and is the most concise (it also has field order, though that's not relevant to postgres), but the vanilla schema may be able to handle more of the gotchas. Though, I don't recall what |
I don't know first hand. But I think the decision to use Open API was integration with Swagger, since it seemed popular. Do either JSON Table Schema or JSON Schema have this? Maybe there were discussions about API description being a misuse of the OPTIONS verb. And that prompted removing support. I don't know. My opinion is that API description is a valid use for the OPTION verb. |
There's a ton of tools out there that use the JSON Schema spec, but probably not an API doc generator. JSON Table Schema is newer, but some tools do exist. |
I agree with @majorcode's excellent detailed assessment that much information in our openapi output is sadly both verbose and incomplete. One thing I liked about the spec was that I could connect interactive api exploring tools to postgrest and it would look pretty. I thought there was a wow factor in seeing a collapsable list of endpoints and verbs with buttons to make the web requests. But the real day-to-day usability outside of a superficial demo is just not that good with the openapi output we have. The most useful output on the root route would probably be a list of the endpoints and the possible embedding combinations. Then each endpoint like I think simplicity usually wins in the long run, even if it feels less exciting than adopting a fancy standard like openapi. I feel kind of embarrassed taking this position since for a long time I was pushing for us to use the standard and a number of people contributed significant work to make it happen. Let the commentary begin. |
I'm in favor of primarily using a PostgREST-specific schema format and secondarily using content negotiation to support other formats like OpenAPI or JSON Table Schema. The Throwing out my two cents, what I'd like from the reflection capabilities of PostgREST is a way to tell that some Creating content types for PostgREST's concepts makes it possible to use content negotiation when asking for schema information. Additionally, it makes it possible to write services that are compatible with multiple systems including PostgREST, since different services will have different content types instead of everyone responding with |
Oh and one other thing: for the root resource, you might consider something like a JSON Home document. |
... I would say this is outside the scope of PostgREST
|
We could remove the self-documentation from postgrest and suggest other standalone database api documenting servers people could use. However our internal code will still be gathering the db structure to handle the requests (embedding etc) so it's kind of a waste to not serve even a simple api self-description endpoint since we have the info and all. True that many people will want the whole graphql thing which seems out of scope, but I was thinking postgrest could serve more like a list of the available tables and procs, and per table/proc a list of columns, args, keys, and foreign keys. |
just the simple list is easy to implement with a view https://gist.github.com/ruslantalpa/b2f10eb1b5f6dd0fc1c154e071a1c91b also some ideas here #13 (comment) |
Reopening, issue contains good points against OpenAPI and with 3.0 it seems to get even more verbose/complex, this doesn't look good for maintenance in the long run. @ruslantalpa Having an user run a separate sql query is redundant work(also could get out of sync with what pgrst exposes) since we already have a structure cached, would be better to expose this structure in a standard way. Even a simple non-standard json structure(like tables with insertable/updatable/deletable permissions) would be useful and easier to work with than OpenAPI format, so this could be another option and let the user transform this structure to OpenAPI/JSON schema/GraphQL in the proxy layer. |
I will just throw this idea here, What if we take out open api spec from the codebase and utilize psql to output information about tables and generate open api spec from it ? This is just an idea, I haven't seen anything like that but I could try to make it. I think it would work like this. I would call describe table with psql and output it into temp file. From that point you have all information about the table and you can generate the open api spec. |
@danielstaleiny Right now we run several sql queries(in this module: DbStructure) that return more information than That being said, the idea of generating the spec in SQL could be a good one. We'd have more flexibility‐it'd be easier to support multiple specs(OpenAPI v2/v3, RAML, etc) and users could customize the format to their needs. The design needs more thought, but we could offer a config option that allows a VIEW(or better yet a MATERIALIZED VIEW) that would be queried when you request the root |
A rough design idea, PostgREST would provide:
|
This is #260 all over again. This is not the job of postgrest (responding with schemas describing the database). If the user wants a openapi spec?... have an endpoint served by php/node/rpc/whatever and change it to your hearts content. Just because postgrest "knows" something about the db, does not mean it has to tell the rest of the world about it, it's internal data. The whole openapi code should be removed altogether from 6.x ( and if anyone is inclined to put in the work, have a documentation page with a plpgsql function reaturing a openapi spec json which ppl can modify in their specific deployment and addapt, it's that simple ) |
I agree that we should deprecate OpenAPI on 6.X, for several reasons, all already discussed over many issues, but it does leave us with an empty root, which I think we can take advantage of it. Taking @steve-chavez idea, we could have the same config option This brings another point that I've been thinking for a while. PostgREST function is and should be simple. Despite that, the world doesn't like simple and there are several useful feature requests that were closed, because indeed they are external to its current function. |
I agree that PostgREST should do one thing and do it well. I am all for leaving creating of specs outside of the scope of the project and I like idea from Qua4tro to make a community/additional-components repos. I see couple of options to create the specs from. Option 1. Rely only on psql and get all the information. (still not sure if possible, convenient) |
It wasn't my intention to cause a breaking change or deprecate the OpenAPI spec, but to provide an easier way to improve/customize it and let the community help us in doing so(since it'll be SQL). Though OpenAPI has many issues, it's still of high value and I've seen PostgREST OpenAPI support cited as one of its main strengths.
@ruslantalpa I think providing resources metadata is well in the scope of REST.
That's exactly the reason why I propose to do it in SQL, to avoid the complexity of all the extra tooling the user has to choose from or implement from scratch and keep the db as the single source of truth.
The problem is that if we don't expose what pgrst knows the custom OpenAPI spec could be invalid, the output could show tables that pgrst doesn't know. Ideally we'd have a way to validate if the spec comes from the schema cache(not sure if feasible for now). Also regarding #260, I don't see how this issue is related to it, but in any case the whole auth feature set started as being harcoded and then pgrst offered a more extendable way to do it in SQL, which is a similar idea to what I proposed.
@Qu4tro Could be a good idea. We could have the OpenAPI SQL there and test it with postgres-json-schema. |
I also worry about exposing our schema cache for customization, I'd prefer not risk losing the guarantees that the PostgREST sandbox offers. So, how about if we leave the schema cache alone for now and just offer a We can recommend creating the |
Users have also been asking about a simpler json spec that can be used to build an admin panel, as shown in #717. So, similarly to |
How about this, we offer a way to override the root response with a custom function that will use our usual RPC interface and take advantage of GUC headers. The function can be like this: create or replace function root() returns jsonb as $_$
begin
-- openapi v2 spec
if current_setting('request.header.accept', true) = 'application/openapi+json' then
set local "response.headers" = '[{"Content-Type": "application/openapiv2+json"}]';
return $$
{
"swagger": "2.0",
"info":{"version":"5.2.0","title":"PostgREST API","description":"This is a dynamic API generated by PostgREST"}
}
$$::jsonb;
else
-- simpler json spec
set local "response.headers" = '[{"Content-Type": "application/json"}]';
return $$
[
{
"table":"items"
},
{
"table":"subitems"
}
]
$$::jsonb;
-- ... other specs
end if;
end
$_$ language plpgsql; Name of the config option could be like The function can even return a composite type and filters could be used, I think this would be the most flexible and future-proof option. |
I like it. Solves keeping swagger intact and allowing for new queries that allow for PostgreREST admin dashboards. How would this default function be distributed? |
@Qu4tro Since this wouldn't break anything I was thinking to have a recommended default function in the docs(function could be in a postgrest-contrib repo as you suggested). We would need to refactor/update this base spec and if possible offer a default openapi function. I think we can slowly figure that out, for now having the possibility to use the root function would help a lot in solving openapi issues. |
A while ago I've noticed we need to refactor the codebase and replace some parts in Haskell with SQL so we can offer a true mirror of our schema cache in the "base spec". This will require some work but it's doable. For now, for anyone that would like to help us constructing the OpenAPI function, the root-spec config is already available(added in #1317) and you can use the base spec, later we can update it with up-to-date schema cache queries. Also you can use https://github.com/gavinwahl/postgres-json-schema for testing. |
@steve-chavez the base spec link is 404'ing, I would really like to help/work on this! |
@bwbroersma I've updated the link to this fork https://gist.github.com/steve-chavez/eae6a67ec81b195c133bcb9ff0c917fb |
In the new version released yesterday, it doesn't seem possible to get the schema definition for a particular table/view using OPTIONS request.
It is possible to get the schema for the entire db by issuing a GET request on the root node, but that seems like an overkill, and too verbose.
It is still noted in the docs though, that OPTIONS is a valid verb.
Also, without OPTIONS, it doesn't seem possible to get information about foreign key constraints (GET request on the root node does not provide this info).
* Using Windows binary
The text was updated successfully, but these errors were encountered: