-
Notifications
You must be signed in to change notification settings - Fork 148
Swagger integration
Compojure-api supports natively Swagger, with help of Ring-Swagger & ring-swagger-ui.
In order to use swagger, a swagger spec needs to be generated. It contains information about the routes, data models and common api capabilities. Optionally a local a swagger ui can be mounted.
There are two way to mount the swagger things:
A route function compojure.api.swagger/swagger-routes
, bundling the spec-route
compojure.api.swagger/swagger-docs
and the ui-route compojure.api.swagger/swagger-ui
.
It takes an optional options map. Without any parameters, it uses defaults to swagger-ui is
mounted to /
and the spec is generated to /swagger.json
.
Note swagger-docs
and swagger-ui
are not part of the public api since 1.0.0. Use swagger-routes
instead.
(api
(swagger-routes)
(GET "/ping" []
(ok {:message "pong"})))
; same as
(api
(swagger-routes
{:ui "/", :spec "/swagger.json"})
(GET "/ping" []
(ok {:message "pong"})))
For larger apps, it's nice to be able to keep all api-level configuration in
one place. If an api has a :swagger
option, swagger-routes
is configured
with its value and mounted before other routes and configured.
(api
{:swagger {:ui "/", :spec "/swagger.json"}}
(GET "/ping" []
(ok {:message "pong"})))
Swagger data is read from the following sources into the generated spec:
- api creation-time route & schema information, generated for you by the lib
- Run-time extra information from the middleware passed in with the request
- User-set custom information
Passed in automatically via request injection.
By default, the application wire-format serialization capabilities (:produces
and :consumes
)
are injected in automatically by the api
machinery.
One can contribute extra arbitrary swagger-data (like swagger security definitions) to the docs via
the ring.swagger.middleware/wrap-swagger-data
middleware.
The swagger-routes
can be used without parameters, but one can also set any valid root-level Swagger Data with it.
(swagger-routes)
(api
{:ring-swagger {:ignore-missing-mappings? true}})
:swagger
{:ui "/api-docs"
:spec "/swagger.json"
:options {:ui {:validatorUrl nil}}
:data {:basePath "/app"
:info {:version "1.0.0"
:title "Thingies API"
:description "the description"
:termsOfService "http://www.metosin.fi"
:contact {:name "My API Team"
:email "[email protected]"
:url "http://www.metosin.fi"}
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}}
:tags [{:name "math", :description "Math with parameters"}
{:name "pizzas", :description "Pizza API"}
{:name "failing", :description "Handling uncaught exceptions"}
{:name "dates", :description "Dates API"}
{:name "responses", :description "Responses demo"}
{:name "primitives", :description "Returning primitive values"}
{:name "context", :description "context routes"}
{:name "echo", :description "Echoes data"}
{:name "ordered", :description "Ordered routes"}
{:name "file", :description "File upload"}]}}}
...)
-
All the possible options under
:swagger
are described in [source code] (https://github.com/metosin/compojure-api/blob/master/src/compojure/api/swagger.clj). -
Ring-swagger also allows customization via
:ring-swagger
option, more in ring-swagger docs. -
Ring-swagger-ui can also configured from here, see swagger-ui options
-
The swagger data itself is described in the swagger spec.
Example to enable HTTP Basic Auth for the swagger-ui:
(api
{:swagger
{:data
{:securityDefinitions
{:login
{:type "basic"}}}}}
...)
Example to enable header-based (x-apikey
) authentication for the swagger-ui:
(api
{:swagger
{:data
{:securityDefinitions
{:api_key
{:type "apiKey"
:name "x-apikey"
:in "header"}}}}}
...)
Example to enable custom authentication for the swagger-ui:
(defn authorized-for-docs? [handler]
(fn [request]
(let [auth-header (get (:headers request) "authorization")]
(cond
(nil? auth-header)
(-> (res/unauthorized)
(res/header "WWW-Authenticate" "Basic realm=\"whatever\""))
(= auth-header "Your Basic Auth Secret")
(handler request)
:else
(res/unauthorized {})))))
(def app
(ring/api
(ring/context "/docs" req
:middleware [authorized-for-docs?]
(docs/swagger-routes
{:ui "/"
:options {:ui {:swagger-docs "/docs/swagger.json"}}
:spec "/swagger.json"
:data {:info
{:title "Title ..."
:description "Foo Bar"}}}))
One can configure Ring-Swagger by providing options to api-middleware
for the key :ring-swagger
. See Ring-Swagger docs for possible options and examples.
(api
{:ring-swagger {:ignore-missing-mappings? true}})
(swagger-routes)
...)
If the shipped ring-swagger-ui isn't enough for you, you can exclude it from dependencies and create & package your own UI from the sources.
Another option is to override the shipped files under resources/swagger-ui
.
Just place a custom index.html
there and it's used instead.
Most in-build compojure-api restucturing also contribute to generated swagger-docs. See Endpoints for details.
(GET "/plus" []
:return Long
:header-params [authorization :- s/String]
:query-params [x :- (describe Long "description")
{y :- Long 1}]
:summary "x+y with query-parameters. y defaults to 1."
(ok {:total (+ x y)}))
; #Route{:path "/plus",
; :method :get,
; :info {:responses {200 {:schema java.lang.Long, :description ""}},
; :parameters {:query {Keyword Any, :x java.lang.Long, #schema.core.OptionalKey{:k :y} java.lang.Long}},
; :summary "x+y with query-parameters. y defaults to 1."}}
Adding a :no-doc true
meta-data on route (or context) marks it to be removed from swagger-docs
(context "/secret" []
:no-doc true
(GET "/abba" []
(ok {:jabba "dabba"})))
You can either use the normal restructuring (:query
, :path
etc.) to get the swagger docs and
disable the coercion by setting the :coercion
to nil
for the whole api, for a context, or for an
individual endpoint.
(api
{:coercion nil}
...
(context "/no-validation" []
:coercion nil
(POST "/echo" []
:return {:x s/Int}
:body [data {:x s/Int}]
(ok data)))
(POST "/echo-no-validation" []
:coercion nil
:return {:x s/Int}
:body [data {:x s/Int}]
(ok data))
You can also use the :swagger
metadata at your route, which pushes the swagger docs for the routes, without doing any destructuring of the request.
(GET "/route" [q]
:swagger {:x-name :boolean
:operationId "echoBoolean"
:description "Echoes a boolean"
:parameters {:query {:q s/Bool}}}
;; q might be anything here.
(ok {:q q}))
As one can pass arbitrary data to swagger, it's good to validate the end results. Swagger 2.0 has a JSON Schema to validate the results against. This can be done in several ways:
See https://github.com/swagger-api/validator-badge for details.
Build your own validation-badge endpoint.
(require '[ring.swagger.validator :as validator])
(validator/validate (slurp "http://localhost:3000/swagger.json"))
; => nil
Great for integration tests.
(Note: this was moved from
compojure.api.swagger
tocompojure.api.validator
in this commit, but the Codox API documentation doesn't reflect the update).
(require '[compojure.api.validator :as validator])
(validator/validate api)
; => returns api or throws descriptive exception