Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unifying Client and Server-side Form Validation with Clojure(Script)

Unifying Client and Server-side Form Validation with Clojure(Script)

At uSwitch we built a Clojure/ClojureScript service to handle the specification of questions in online forms and the validation of the user’s answers. This combination of client and server side code has been an effective solution to some of the problem we had. In this presentation we talk about the sweet spot we found for applying Clojure/ClojureScript in combination and the lessons learned along the way.

Jason Neylon

April 29, 2015
Tweet

More Decks by Jason Neylon

Other Decks in Technology

Transcript

  1. Problem(s) - Functional - What questions to ask to customers

    - Are customer’s answers valid - Technical - Client side validation required - Multiple apps need to ask consistent questions
  2. Solution (Server side) Supplier Questions Service (Prismatic Schema) which questions

    to ask? questions answers valid? validation errors Call Center Main Website
  3. Solution (Client side) Supplier Questions Service (Prismatic Schema) Main Website

    Call Center CLJS Library CLJS Library Deliver core engine built using ClojureScript (JavaScript library)
  4. #win • In production & stable • Trivial to make

    and deploy simple changes • Reduced cross team work & coordination overhead
  5. • cljx - extra build step is slow • cljs

    != clj (dates | parsing numbers | logging) • Reimplement rendering for each application • Served as separate file - additional asset request • Will this break the rest of my JavaScript/the website? No free lunch
  6. (defquestions questions [{:key :date-of-birth :type :date :group groups/about-you :schema {:date-of-birth

    (shared/date-of-birth 18)} :label "What is your date of birth?" :help-text "The following information is required by your new supplier for security. Your answers will ensure that your new supplier can verify your identity, for example when enquiring over the telephone." ...])
  7. (defmacro defquestions "Macro to reduce the size of the generated

    javascript by omitting unused keys (and their values) in the questions. [...]" [name questions] {:pre [(symbol? name) (and (coll? questions) (every? map? questions))]} (let [filtered-questions (mapv #(select-keys % [:key :tags :schema :ask-if]) questions)] `(def ~name ~filtered-questions)))
  8. {:key :eon-reward :type :yes-no :schema {:eon-reward shared/Yes-No} :label "Would you

    like to sign up to E.ON rewards?" :description "..." :help-text "..."} {:key :tesco-clubcard-number :type :text :ask-if (fn [answers] (contains? shared/yes-values (:eon-reward answers))) :schema {(s/optional-key :tesco-clubcard-number) TescoClubcard} :label "Please enter your Tesco Clubcard details, ..." :description "..." :help-text "..."}
  9. {:key :eon-reward :type :yes-no :schema {:eon-reward shared/Yes-No} :label "Would you

    like to sign up to E.ON rewards?" :description "..." :help-text "..."} {:key :tesco-clubcard-number :type :text :ask-if (fn [answers] (contains? shared/yes-values (:eon-reward answers))) :schema {(s/optional-key :tesco-clubcard-number) TescoClubcard} :label "Please enter your Tesco Clubcard details, ..." :description "..." :help-text "..."}
  10. {:key :eon-reward :type :yes-no :schema {:eon-reward shared/Yes-No} :label "Would you

    like to sign up to E.ON rewards?" :description "..." :help-text "..."} {:key :tesco-clubcard-number :type :text :ask-if (fn [answers] (contains? shared/yes-values (:eon-reward answers))) :schema {(s/optional-key :tesco-clubcard-number) TescoClubcard} :label "Please enter your Tesco Clubcard details, ..." :description "..." :help-text "..."}
  11. More details on the blog post: http://www.uswitch.com/tech/ Find us on

    Twitter: @jasonneylon ( Jason ) @velrok ( Waldemar ) @uSwitchEng ( uSwitch ) Thank you