Validator

Coast makes it simple to validate input with the help of a validate function.

In this guide you learn how to validate data manually.

NOTE: Coast validation uses verily under the hood. For full usage details, see the official verily documentation.

Validating Input

Let's start with the example of validating input received via HTML form:

Make the routes to show the form and handle the submission:

; src/routes.clj

(def routes
  (coast/site
    [:get "/posts/:post-id/edit" :post/edit]
    [:put "/posts/:post-id" :post/change]
    [:get "/posts/:post-id" :post/view])))

Make the handler functions to show the form

; src/customer.clj

(defn edit [{:keys [params errors]}]
  [:div
    (when (some? errors)
      [:div
        [:div (:customer/email errors)]])
    (coast/form-for ::change {:customer/id (:customer/id params)}
      [:input {:type "text" :name "customer/email"}]
      [:button {:type "submit"} "Submit"])])

...handle the form submission and use the validator to validate the data:

; src/customer.clj

(defn change [{:keys [params]}]
  (let [[_ errors] (-> (select-keys params [:customer/id :customer/email])
                       (coast/validate [[:email [:customer/email]]])
                       (coast/rescue))]
    (if (nil? errors)
      (coast/redirect-to ::view {:customer/id (:customer/id request)})
      (edit (merge request errors)))))

Let's break down the above code into small steps:

  1. We destructured the request map into a params variable
  2. We used the validate method to validate the params data against an :email rule
  3. If validation fails, the form re-renders with any errors
  4. If it succeeds, it redirects to the ::view handler

Common Rules

Below is the list of available, built in validator rules

(validate map rules)

Validate required keys and one email key:

(coast/validate {:customer/id 123
                 :customer/email "sean@example.com"} [[:required [:customer/id :customer/email]]
                                                      [:email [:customer/email]]])

NOTE: You can optionally pass custom error messages to return when your validation fails as the third value in each vector:

(coast/validate {} [[:required [:customer/id] "can't be blank"]
                    [:email [:customer/email] "needs to be an actual email"]])

The exception that is raised from the above failed (coast/validate) validation looks like this:

{:customer/email "Email needs to be an actual email"
 :customer/id "Id can't be blank"}