# Data Validation in Clojure

[Malli](https://github.com/metosin/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](https://blog.tvaisanen.com/typescript-like-intellisense-for-clojure-functions-with-malli) for static type checking in editors.
    

Malli uses [hiccup-inspired](https://github.com/weavejester/hiccup) vector syntax for defining types. There's also a [map syntax variant](https://cljdoc.org/d/metosin/malli/0.8.9/doc/readme#map-syntax) but the use case for that is to be used as an internal library representation. The vector syntax looks like this.

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

Here we have a "Todo" schema that represents a [map](https://clojure.org/reference/data_structures#Maps) 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.

```clojure
(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.

```clojure
(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`.

```clojure
(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](https://cljdoc.org/d/metosin/malli/0.13.0/doc/readme) 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.
