Data Validation in Clojure

A Short Introduction to Malli

Data Validation in Clojure

Malli is a data-driven schema Clojure library for defining types and validating data. It can be used for example to:

  • type check data when received from untrusted sources i.e. validate that HTTP request bodies before writing to the database

  • define domain entities as code and generate UML diagrams

  • generate clj-kondo types for static type checking in editors.

Malli uses hiccup-inspired vector syntax for defining types. There's also a map syntax variant but the use case for that is to be used as an internal library representation. The vector syntax looks like this.

  (def Todo
    [:map
     [:id :int]
     [:author :string]
     [:created inst?]
     [:status [:enum :todo :doing :done]]])

Here we have a "Todo" schema that represents a map with properties: id, author, created, and status. Each child of the map vector defines a property, meaning the property :id is a type of :int and :created is a type of instant and so on. All of these properties can be validated separately.

(malli.core/validate :int 1)
;; => true
(malli.core/validate :int "1")
;; => false
(malli.core/validate inst? (java.time.Instant/now))
;; => true
(malli.core/validate [:enum :todo :doing :done] :done)
;; => true

Or as a whole.

(malli.core/validate Todo
                    {:id 1 
                     :author "Toni" 
                     :created  #inst "2023-09-30"
                     :status :todo})
;; => true

In the case that the data is not valid we probably want to know the details on why so. For this, we can use malli.core/explain and malli.error/humanize.

(def invalid-todo
    {:id 1
     :author "Toni" 
     :created "not-an-instant"
     :status :todo})

(malli.core/validate Todo invalid-todo)
;; => false
(:errors (malli.core/explain Todo invalid-todo))
;; =>
({:path [:created],
  :in [:created],
  :schema inst?,
  :value "not-an-instant"})

(malli.error/humanize
  (malli.core/explain Todo invalid-todo))
;; => 
{:created ["should be an inst"]}

The error messages can be customized and internationalized to fit your needs. Malli has a lot more features to offer, so I encourage you to go through the documentation to learn more.

Thank you for reading, I hope you found this useful. Please let me know if there's some specific Malli feature that you would like to know more about.