feat: add x-postgrest-typegen-metadata to openapi#6
Conversation
cf5933b to
9c25e09
Compare
Opt-in `openapi-metadata` config that adds a top-level `x-postgrest-typegen-metadata` vendor extension to the OpenAPI output, carrying catalog-grade metadata not otherwise expressible in OpenAPI: object kind, identity/generated columns, the full relationship graph with cardinality, function return types (incl. OUT/TABLE columns), composite types and computed fields. This lets clients (e.g. @supabase/postgrest-typegen) generate faithful types from the OpenAPI document alone, with no database connection. Disabled by default; standard OpenAPI consumers ignore the extra x- key.
9c25e09 to
fb3a733
Compare
| Typegen metadata extension | ||
| -------------------------- | ||
|
|
||
| PostgREST's OpenAPI is a name-based description of the API surface, which is lossy for client type generation (it cannot distinguish tables from views, identity columns, function return types, composite types, etc.). To support faithful type generation directly from the OpenAPI document, set ``openapi-metadata`` to ``true``. PostgREST then adds a top-level ``x-postgrest-typegen-metadata`` `vendor extension <https://spec.openapis.org/oas/v2.0#specification-extensions>`_ to the output, carrying catalog-grade metadata not otherwise expressible in OpenAPI: |
There was a problem hiding this comment.
@laurenceisla Since you've done a lot of work on OpenAPI, is x-postgrest-typegen-metadata our only option for this new metadata?
There was a problem hiding this comment.
From what I can see, there are some overlapping fields between x-postgrest-typegen-metadata and regular OpenAPI/JSONSchema like nullable, enum, default, etc. for tables and views (and some others for functions). So, an alternative would be to add these x-<my-custom-field> to each Object (parameter, path, etc.), similar to what's proposed here. However the relationships field has more info that may be difficult to express in the OpenAPI output, I can't find a way to easily convey this rn.
I guess having all the relevant info inside the x-postgrest-typegen-metadata is an easier way to retrieve the data that is needed. However, I think we should still check if there's a way to avoid doing this and using the default OpenAPI output instead to avoid duplicity. But, if there's no alternative then yes, the custom field would be needed.
There was a problem hiding this comment.
The motivation is on slack (private link)
From a comment there:
I'm not sure if OpenAPI is the right approach since it might not support all usecases needed by the typegen package. I think it's easier if each typegen target has its own SQL code you run against the db and it builds the output from that.
I wonder if having https://github.com/PostgREST/postgrest-openapi would make the creation of custom fields like this one easier (since it's using SQL directly). The customers that need to modify the output wouldn't need to wait for a PR to be approved or to check if PostgREST would allow it in core in the first place.
| -- | Distinct-by-key, preserving first occurrence. | ||
| nubOn :: Eq b => (a -> b) -> [a] -> [a] | ||
| nubOn f = go [] | ||
| where | ||
| go _ [] = [] | ||
| go seen (x:xs) | ||
| | f x `elem` seen = go seen xs | ||
| | otherwise = x : go (f x : seen) xs | ||
|
|
||
| -- | Map a pg regtype text (SQL spelling, possibly schema-qualified) to the | ||
| -- pg_catalog short type name the typegen generator expects (int8, _text, …). | ||
| shortType :: Text -> Text | ||
| shortType raw | ||
| | "[]" `T.isSuffixOf` raw = "_" <> shortType (T.dropEnd 2 raw) | ||
| | otherwise = maybe bare snd (find ((== bare) . fst) sqlToShort) | ||
| where | ||
| bare = T.takeWhileEnd (/= '.') raw | ||
| sqlToShort = | ||
| [ ("integer", "int4"), ("bigint", "int8"), ("smallint", "int2") | ||
| , ("boolean", "bool"), ("double precision", "float8"), ("real", "float4") | ||
| , ("character varying", "varchar"), ("character", "bpchar") | ||
| , ("timestamp with time zone", "timestamptz") | ||
| , ("timestamp without time zone", "timestamp") | ||
| , ("time with time zone", "timetz"), ("time without time zone", "time") ] |
There was a problem hiding this comment.
Could we move this Haskell logic to SQL?
There was a problem hiding this comment.
These are Postgres types, right? Then yeah, I think what this is doing is taking the long names that PostgREST retrieves from PostgreSQL and then converting them to short names. So there's no need to do this transformation if the schema cache query also takes the short names alongside the long names.
Add additional metadata to the openapi specs endpoint from postgrest.
A new
x-postgrest-typegen-metadatablock allowingpostgrest-jstype generation to be totally driven by a PostgREST endpoint.Related: supabase/pg-toolbelt#303