clojureringreitit

How do you set up middleware in Clojure using Reitit to enable coercion of body params?


I am trying to set a a Reitit router that performs coercion. I can get the response section working but I can't seem to get the body coercion to work properly. The following is the code I am using:

(ns example
  (:require
   [ring.middleware.json :refer [wrap-json-body wrap-json-response]]
   [ring.middleware.reload :refer [wrap-reload]]
   [ring.util.response :refer [response]]
   [reitit.ring.coercion :as rrc]
   [reitit.coercion.malli]
   [reitit.ring :as ring]))

(def router
  (ring/ring-handler
   (ring/router
    [["/healthz" {:get (fn [_] {:status 200 :body "healthy"})}]
     ["/api" {:coercion reitit.coercion.malli/coercion
              :middleware [rrc/coerce-exceptions-middleware
                           rrc/coerce-request-middleware
                           rrc/coerce-response-middleware]}
      ["/messages" {:post {:summary "Add a new message"
                           :parameters {:body [:map [:name string?]]}
                           :responses {200 {:body [:map
                                                   [:message string?]]}}
                           :handler (fn [req]
                                      {:status 200 :body {:message (:name (:body req))}})}}]]]
    {:data {:middleware []}})
   (ring/create-default-handler)))

(def app (-> #'router
             (wrap-reload)
             (wrap-json-response)
             (wrap-json-body {:keywords? true :bigdecimals? true})))

The coercion is failing even when I send the correct body parameters with the following error:

{
  "schema": "[:map {:closed true} [:name string?]]",
  "errors": [
    {
      "path": [],
      "in": [],
      "schema": "[:map {:closed true} [:name string?]]",
      "value": null,
      "type": "malli.core/invalid-type",
      "message": "invalid type"
    }
  ],
  "value": null,
  "type": "reitit.coercion/request-coercion",
  "coercion": "malli",
  "in": [
    "request",
    "body-params"
  ],
  "humanized": [
    "invalid type"
  ]
}

This seems to be indicating that the coercion can't find any body parameters. I assume this has to do with the way I am setting up my middleware but I can't seem to fix this. How should I set the middleware up so the Reitit coercion works properly in this case?


Solution

  • I was able to get coercion to work by removing the wrap-json-response and wrap-json-body middleware and replacing it with the muuntaja middleware as follows:

    (def router
      (ring/ring-handler
       (ring/router
        [["/healthz" {:get (fn [_] {:status 200 :body "healthy"})}]
         ["/api" {:coercion reitit.coercion.malli/coercion
                  :middleware []}
          ["/messages" {:name ::message
                        :post {:summary "Add a new message"
                               :parameters {:body [:map [:name string?]]}
                               :responses {200 {:body [:map
                                                       [:message string?]]}}
                               :handler (fn [{:keys [parameters]}]
                                          {:status 200 :body {:message (:name (:body parameters))}})}}]]]
        {:data {:muuntaja m/instance
                :middleware [params/wrap-params
                             muuntaja/format-middleware
                             rrc/coerce-exceptions-middleware
                             rrc/coerce-request-middleware
                             rrc/coerce-response-middleware]}})
       (ring/create-default-handler)))