- Notifications
You must be signed in to change notification settings - Fork147
Resources and Liberator
Marcin Kulik edited this pageJan 8, 2017 ·4 revisions
Compojure-api also allows http-endpoints to be modeled as data-driven resources, using enhancedRing-Swagger resource definitions. Resources might be good in the following cases:
- Wanting to add swagger-docs and/or schema-based coercion to your existing resource-based apis, like theLiberator.
- Generate endpoints based on external data, e.g.
- generate REST or CRUD-apis from (database) entity definitions
- generate RPC-apis from function/data definitions
Resources are created with functioncompojure.api.resource/resource, which is also imported in thecompojure.api.sweet namespace. Both request & response coercion can be disabled for resources, so that they can be used just to generate swagger-docs on top of existing/legacy apis.
Resources are routed withcontext.
(context"/hello" []:middleware [[require-role:admin]] (resource {:description"hello-resource":responses {200 {:schema {:message s/Str}}}:post {:summary"post-hello":parameters {:body-params {:name s/Str}}:handler (fnk [[:body-params name]] (ok {:message (format"hello, %s!" name)}))}:get {:summary"get-hello":parameters {:query-params {:name s/Str}}:handler (fnk [[:query-params name]] (ok {:message (format"hello, %s!" name)}))}}))
(defnresource"Creates a nested compojure-api Route from enhanced ring-swagger operations map and options. By default, applies both request- and response-coercion based on those definitions. Options: - **:coercion** A function from request->type->coercion-matcher, used in resource coercion for :body, :string and :response. Setting value to `(constantly nil)` disables both request- & response coercion. See tests and wiki for details. Enhancements to ring-swagger operations map: 1) :parameters use ring request keys (query-params, path-params, ...) instead of swagger-params (query, path, ...). This keeps things simple as ring keys are used in the handler when destructuring the request. 2) at resource root, one can add any ring-swagger operation definitions, which will be available for all operations, using the following rules: 2.1) :parameters are deep-merged into operation :parameters 2.2) :responses are merged into operation :responses (operation can fully override them) 2.3) all others (:produces, :consumes, :summary,...) are deep-merged by compojure-api 3) special key `:handler` either under operations or at top-level. Value should be a ring-handler function, responsible for the actual request processing. Handler lookup order is the following: operations-level, top-level. 4) request-coercion is applied once, using deep-merged parameters for a given operation or resource-level if only resource-level handler is defined. 5) response-coercion is applied once, using merged responses for a given operation or resource-level if only resource-level handler is defined. Note: Swagger operations are generated only from declared operations (:get, :post, ..), despite the top-level handler could process more operations. Example: (resource {:parameters {:query-params {:x Long}} :responses {500 {:schema {:reason s/Str}}} :get {:parameters {:query-params {:y Long}} :responses {200 {:schema {:total Long}}} :handler (fn [request] (ok {:total (+ (-> request :query-params :x) (-> request :query-params :y))}))} :post {} :handler (constantly (internal-server-error {:reason\"not implemented\"}))})" ([info] (resource info {})) ([info options] (let [info (merge-parameters-and-responses info) root-info (swaggerize (root-info info)) childs (create-childs info) handler (create-handler info options)] (routes/createnilnil root-info childs handler))))
(resource {:handler (constantly (ok"hello world"))})
(resource {:description"shared description, can be overridden":parameters {:path-params {:id Long}}:get {:description"get user":summary"get-user ftw!":handler get-user}:put {:parameters {:body-params User}:responses {200 {:schema User}}:handler modify-user}:delete {:description"delete's a user":handler delete-user}:handler (constantly (internal-server-error {:reason"other methods not implemented"}))})
Inlined (as a closure):
(context"/plus" []:middleware [require-admin] (resource {:get {:parameters {:query-params {:x Long,:y Long}}:responses {200 {:schema {:total Long}}}:handler my-plus-request-handler-with-coerced-inputs-and-outputs}}))
Predefined (bit faster):
(defplus-resource (resource {:get {:parameters {:query-params {:x Long,:y Long}}:responses {200 {:schema {:total Long}}}:handler my-plus-request-handler-with-coerced-inputs-and-outputs}}))(context"/plus" []:middleware [require-admin] plus-resource)
- https://github.com/metosin/compojure-api/tree/master/examples/resources
- https://github.com/metosin/compojure-api/tree/master/examples/reusable-resources
- set the Liberator resource as top-level handler
- return raw responses to enable response coercion via
:as-responseorring-response, see theguide - read the coerced parameters from the liberator context:
(get-in ctx [:request :body-parameters]) - optionally disable coercion on resource just to get just swagger-docs
(defuser-resource (resource {:parameters {:path-params {:id Long}}:get {:parameters {:query-params {(s/optional-key:fields) [String]}}:responses {200 {:schema User}}:summary"returns a User (or fields of it)"}:put {:parameters {:body-params User}:responses {200 {:schema User}}:summary"Updates a user"}:delete {:summary"Deletes a user"}:handler my-liberator-resource};; add this if you just want the swagger-docs {:coercion (constantlynil)}))(context"/user" [] user-resource)
Feel free to update this guide. Initial discussionhere.