Skip to content

Commit

Permalink
doc: improve openapi docs
Browse files Browse the repository at this point in the history
  • Loading branch information
opqdonut committed Sep 16, 2024
1 parent 610586f commit afc8945
Showing 1 changed file with 105 additions and 62 deletions.
167 changes: 105 additions & 62 deletions doc/ring/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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
Expand All @@ -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]]}}
```

0 comments on commit afc8945

Please sign in to comment.