reitit/doc/coercion/malli_coercion.md
Juho Teperi 0ca4fce984 Add test-doc-blocks
I suspected the malli coercion custom registry example was broken, so I
wanted to get it tested and the coercion Malli options indeed had an
extra layer of map.
2026-02-09 14:18:14 +02:00

3.9 KiB

Malli Coercion

Malli is data-driven Schema library for Clojure/Script.

Default Syntax

By default, Vector Syntax is used:

(require '[reitit.coercion.malli])
(require '[reitit.coercion :as coercion])
(require '[reitit.core :as r])

(def router
  (r/router
    ["/:company/users/:user-id" {:name :user/user-view
                                 :coercion reitit.coercion.malli/coercion
                                 :parameters {:path [:map
                                                     [:company string?]
                                                     [:user-id int?]]}}]
    {:compile coercion/compile-request-coercers}))

(defn match-by-path-and-coerce! [path]
  (if-let [match (r/match-by-path router path)]
    (assoc match :parameters (coercion/coerce! match))))

Successful coercion:

(-> (into {} (match-by-path-and-coerce! "/metosin/users/123"))
    (dissoc :result :data))
;; => {:template "/:company/users/:user-id",
;;     :path-params {:company "metosin", :user-id "123"},
;;     :parameters {:path {:company "metosin", :user-id 123}}
;;     :path "/metosin/users/123"}

Failing coercion:

(try
  (match-by-path-and-coerce! "/metosin/users/ikitommi")
  (catch clojure.lang.ExceptionInfo e (.getMessage e)))
;; => "Request coercion failed"

Lite Syntax

Same using Lite Syntax:

(def router
  (r/router
    ["/:company/users/:user-id" {:name ::user-view
                                 :coercion reitit.coercion.malli/coercion
                                 :parameters {:path {:company string?
                                                     :user-id int?}}}]
    {:compile coercion/compile-request-coercers}))

Configuring coercion

Using create with options to create the coercion instead of coercion:

(require '[malli.util :as mu])

(reitit.coercion.malli/create
  {:transformers {:body {:default reitit.coercion.malli/default-transformer-provider
                         :formats {"application/json" reitit.coercion.malli/json-transformer-provider}}
                  :string {:default reitit.coercion.malli/string-transformer-provider}
                  :response {:default reitit.coercion.malli/default-transformer-provider
                             :formats {"application/json" reitit.coercion.malli/json-transformer-provider}}}
   ;; set of keys to include in error messages
   :error-keys #{:type :coercion :in #_:schema :value #_:errors :humanized #_:transformed}
   ;; support lite syntax?
   :lite true
   ;; schema identity function (default: close all map schemas)
   :compile mu/closed-schema
   ;; validate request & response
   :validate true
   ;; top-level short-circuit to disable request & response coercion
   :enabled true
   ;; strip-extra-keys (affects only predefined transformers)
   :strip-extra-keys true
   ;; add/set default values
   ;; Can be false, true or a map of options to pass to malli.transform/default-value-transformer,
   ;; for example {:malli.transform/add-optional-keys true}
   :default-values true
   ;; encode-error
   :encode-error nil
   ;; malli options
   :options nil})

Configuring humanize error messages

Malli humanized error messages can be configured using :options :errors:

(reitit.coercion.malli/create
 {:options
  {:errors (assoc malli.error/default-errors
                  :malli.core/missing-key {:error/message {:en "MISSING"}})}})

See the malli docs for more info.

Custom registry

Malli registry can be configured conveniently via :options :registry:

(require '[malli.core :as m])

(def coercion
  (reitit.coercion.malli/create
   {:options
    {:registry (merge (m/default-schemas)
                      {:my-type :int})}}))

(def coercer (reitit.coercion/-request-coercer coercion :string [:map [:x :my-type]]))

(coercer {:x "5"} :default)
=> {:x 5}