diff --git a/doc/ring/openapi.md b/doc/ring/openapi.md index 620ec1b4..ec3d2264 100644 --- a/doc/ring/openapi.md +++ b/doc/ring/openapi.md @@ -34,52 +34,6 @@ Coercion keys also contribute to the docs: | :request | optional description of body parameters, possibly per content-type | :responses | optional descriptions of responses, in a format defined by coercion -## Annotating schemas - -You can use malli properties, schema-tools data or spec-tools data to -annotate your models with examples, descriptions and defaults that -show up in the OpenAPI spec. - -Malli: - -```clj -["/plus" - {:post - {:parameters - {:body [:map - [:x - {:title "X parameter" - :description "Description for X parameter" - :json-schema/default 42} - int?] - [:y int?]]}}}] -``` - -Schema: - -```clj -["/plus" - {:post - {:parameters - {:body {:x (schema-tools.core/schema s/Num {:description "Description for X parameter" - :openapi/example 13 - :openapi/default 42}) - :y int?}}}}] -``` - -Spec: - -```clj -["/plus" - {:post - {:parameters - {:body (spec-tools.data-spec/spec ::foo - {:x (schema-tools.core/spec {:spec int? - :description "Description for X parameter" - :openapi/example 13 - :openapi/default 42}) - :y int?}}}}}] -``` ## Per-content-type coercions @@ -91,8 +45,8 @@ openapi example](../../examples/openapi). ```clj ["/pizza" {:get {:summary "Fetch a pizza | Multiple content-types, multiple examples" - :responses {200 {:content {"application/json" {:description "Fetch a pizza as json" - :schema [:map + :responses {200 {:description "Fetch a pizza as json or EDN" + :content {"application/json" {:schema [:map [:color :keyword] [:pineapple :boolean]] :examples {:white {:description "White pizza with pineapple" @@ -101,8 +55,7 @@ openapi example](../../examples/openapi). :red {:description "Red pizza" :value {:color :red :pineapple false}}}} - "application/edn" {:description "Fetch a pizza as edn" - :schema [:map + "application/edn" {:schema [:map [:color :keyword] [:pineapple :boolean]] :examples {:red {:description "Red pizza with pineapple" @@ -115,16 +68,6 @@ and `:openapi/response-content-types` keys, which must contain vector of supported content types. If there is no Muuntaja instance, and these keys are not defined, the content types will default to `["application/json"]`. -## Custom OpenAPI data - -The `:openapi` route data key can be used to add top-level or -route-level information to the generated OpenAPI spec. This is useful -for providing `"securitySchemes"` or other OpenAPI keys that are not -generated automatically by reitit. - -See [the openapi example](../../examples/openapi) for a working -example of `"securitySchemes"`. - ## OpenAPI spec Serving the OpenAPI specification is handled by @@ -148,6 +91,106 @@ If you need to post-process the generated spec, just wrap the handler with a cus ## Swagger-ui -[Swagger-UI](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. +[Swagger-UI](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. See `reitit.swagger-ui/create-swagger-ui-handle` + +## Finetuning the OpenAPI output + +There are a number of ways you can specify extra data that gets +included in the OpenAPI spec. + +### Custom OpenAPI data + +The `:openapi` route data key can be used to add top-level or +route-level information to the generated OpenAPI spec. + +A straightforward use case is adding `"externalDocs"`: + +```clj +["/account" + {:get {:summary "Fetch an account | Recursive schemas using malli registry, link to external docs" + :openapi {:externalDocs {:description "The reitit repository" + :url "https://github.com/metosin/reitit"}} + ...}}] +``` + +In a more complex use case is providing `"securitySchemes"`. See +[the openapi example](../../examples/openapi) for a working example of +`"securitySchemes"`. See also the +[OpenAPI docs](https://spec.openapis.org/oas/v3.1.0.html#security-scheme-object) + +### Annotating schemas + +You can use malli properties, schema-tools data or spec-tools data to +annotate your models with examples, descriptions and defaults that +show up in the OpenAPI spec. + +This approach lets you add additional keys to the +[OpenAPI Schema Objects](https://spec.openapis.org/oas/v3.1.0.html#schema-object). +The most common ones are default and example values for parameters. + +Malli: + +```clj +["/plus" + {:post + {:parameters + {:body [:map + [:x + {:title "X parameter" + :description "Description for X parameter" + :json-schema/default 42} + int?] + [:y int?]]}}}] +``` + +Schema: + +```clj +["/plus" + {:post + {:parameters + {:body {:x (schema-tools.core/schema s/Num {:description "Description for X parameter" + :openapi/example 13 + :openapi/default 42}) + :y int?}}}}] +``` + +Spec: + +```clj +["/plus" + {:post + {:parameters + {:body (spec-tools.data-spec/spec ::foo + {:x (schema-tools.core/spec {:spec int? + :description "Description for X parameter" + :openapi/example 13 + :openapi/default 42}) + :y int?}}}}}] +``` + +### Adding examples + +Adding request/response examples have been mentioned above a couple of times +above. Here's a summary of the different ways to do it: + +1. Add an example to the schema object using a `:openapi/example` + (schema, spec) or `:json-schema/example` (malli) key in your + schema/spec/malli model metadata. See the examples above. +2. Use `:example` (a single example) or `:examples` (named examples) + with per-content-type coercion. -Note: you need Swagger-UI 5 for OpenAPI 3.1 support. As of 2023-03-10, a v5.0.0-alpha.0 is out. +**Caveat!** When adding examples for query parameters (or headers), +you must add the examples to the individual parameters, not the map +schema surrounding them. This is due to limitations in how OpenAPI +represents query parameters. + +```clj +;; Wrong! +{:parameters {:query [:map + {:json-schema/example {:a 1}} + [:a :int]]}} +;; Right! +{:parameters {:query [:map + [:a {:json-schema/example 1} :int]]}} +```