{"id":12711,"date":"2015-10-20T01:00:00","date_gmt":"2015-10-20T00:00:00","guid":{"rendered":"http:\/\/pjagielski.github.com\/2015\/10\/20\/clojure-web-dev-state-2"},"modified":"2023-04-27T11:32:21","modified_gmt":"2023-04-27T09:32:21","slug":"clojure-web-development-state-of-the-art-part-2","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2015\/10\/20\/clojure-web-development-state-of-the-art-part-2\/","title":{"rendered":"Clojure web development &#8211; state of the art &#8211; part 2"},"content":{"rendered":"<p>This is part 2 of my \u201cClojure web development\u201d series. You can discuss first part on <a href=\"https:\/\/www.reddit.com\/r\/Clojure\/comments\/3lgkj7\/clojure_web_development_state_of_the_art\/\">this<\/a> reddit thread. After reading the comments I must explain two assumptions I had writing this series:<\/p>\n<ul>\n<li>Keep things easy to understand for people from outside Clojure land, especially Java devs. That\u2019s why I use REST\/JSON in favor of transit and component as a \u201cdependency injection\u201d implementation which could be easily explained as Spring framework equivalent. The same goes with Om which is a bit verbose, but in my opinion it\u2019s easier to understand for a start and has wider adoption than the other React wrappers.<\/li>\n<li>Keep things easy to bootstrap on a developer machine. This is a hands-on walkthrough and all the individual steps have been committed to GitHub. That\u2019s why I use MongoDB, which could not be the best choice for scaling your application to millions of users, but it\u2019s perfect for bootstrapping &#8211; no schema, tables, just insert data and start working. I highly recommend Honza Kral <a href=\"https:\/\/www.youtube.com\/watch?v=W6J_jz6oifs\">polyglot persistence<\/a> talk, where he encourages starting simple and optimize for developer happiness at the start of a project.<\/li>\n<\/ul>\n<p>In previous post we bootstrapped a basic web application serving REST data with (static for now) Clojurescript frontend, all fully reloadable thanks to reloaded repl and figwheel. You can find final working version of it in <a href=\"https:\/\/github.com\/pjagielski\/modern-clj-web\/tree\/part1\">this branch<\/a>.<\/p>\n<p>Today we\u2019re going to display contact list stored in MongoDB. I assume you have MongoDB installed, if not &#8211; it\u2019s trivial with <a href=\"https:\/\/github.com\/dockerfile\/mongodb\">docker<\/a>.<\/p>\n<h2 id=\"serving-contact-list-from-database\">Serving contact list from database<\/h2>\n<p>OK, let\u2019s start. In the backend we need to add some dependencies to project.clj:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">:dependencies\u00a0...\u00a0[org.danielsz\/system\u00a0\"0.1.9\"]\u00a0[com.novemberain\/monger\u00a0\"2.0.0\"]]<\/pre>\n<\/div>\n<p><code>monger<\/code> is an idiomatic Clojure wrapper for Mongo Java driver and <code>system<\/code> is a nice collection of components for various datastores, including Mongo (a bit like Spring Data for Spring).<\/p>\n<p>In order to interact with a data store I like to use the concept of abstract repository. This should hide the implementation details from the invoker and allows to switch to another store in the future. So let\u2019s create an abstract interface (in Clojure &#8211; protocol) in <code>components\/repo.clj<\/code>:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(ns modern-clj-web.component.repo) (defprotocol ContactRepository (find-all [this]))<\/pre>\n<\/div>\n<p>We need this as a parameter to allow Clojure runtime dispatching correct implementation of this repository. Mongo implementation with Monger is really simple:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(ns modern-clj-web.component.repo (:require [monger.collection :as mc] [monger.json])) ... (defrecord ContactRepoComponent [mongo] ContactRepository (find-all [this] (mc\/find-maps (:db mongo) \"contacts\"))) (defn new-contact-repo-component [] (-&gt;ContactRepoComponent {}))<\/pre>\n<\/div>\n<p>Things to notice here:<\/p>\n<ul>\n<li><code>mc\/find-maps<\/code> just returns all records from collection as Clojure maps<\/li>\n<li><code>ContactComponent<\/code> gets injected with mongo component created by system library, which adds Mongo client under <code>:db<\/code> key<\/li>\n<li>Since this component is stateless, we don\u2019t need to implement <code>component\/Lifecycle<\/code> interface, but it still can be wired in system like a typical lifecycle-aware component<\/li>\n<li>Requiring <code>monger.json<\/code> adds JSON serialization support for Mongo types (e.g. <code>ObjectId<\/code>)<\/li>\n<\/ul>\n<p>Ok, it\u2019s now time to use our new component in the endpoint\/example.clj:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(:require ... [modern-clj-web.component.repo :as r]) (defn example-endpoint [{ repo :contact-repo}] (routes ... (GET \"\/contacts\" [] (response (r\/find-all repo)))<\/pre>\n<\/div>\n<p>The <code>{repo :contact-repo}<\/code> notation (destructuring) automatically binds <code>:contact-repo<\/code> key from system map to the <code>repo<\/code> value. So we need to assign our component to that key in <code>system.clj<\/code>:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(:require ... [modern-clj-web.component.repo :refer [new-contact-repo-component]] [system.components.mongo :refer [new-mongo-db]]) (-&gt; (component\/system-map :app (handler-component (:app config)) :http (jetty-server (:http config)) :example (endpoint-component example-endpoint) :mongo (new-mongo-db (:mongo-uri config)) :contact-repo (new-contact-repo-component)) (component\/system-using {:http [:app] :app [:example] :example [:contact-repo] :contact-repo [:mongo]}))))<\/pre>\n<\/div>\n<p>In short &#8211; we use system\u2019s <code>new-mongo-db<\/code> to create Mongo component, make it a dependency to repository which itself is a dependency of example endpoint.<\/p>\n<p>And finally we need to configure <code>:mongo-uri<\/code> config property in <code>config.clj<\/code>:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(def environ {:http {:port (some-&gt; env :port Integer.)}} :mongo-uri \"mongodb:\/\/localhost:27017\/contacts\"})<\/pre>\n<\/div>\n<p>To check if it works fine, restart repl, type <code>(go)<\/code> again and make a GET to <a href=\"http:\/\/localhost:3000\/contacts\">http:\/\/localhost:3000\/contacts<\/a>.<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">curl http:\/\/localhost:3000\/contacts []<\/pre>\n<\/div>\n<p>OK, so we got empty list since we don\u2019t have any data in Mongo database. Let\u2019s add some with mongo console:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">mongo localhost:27017\/contacts MongoDB shell version: 2.4.9 connecting to: localhost:27017\/contacts &gt; db.contacts.insert({firstname: \"Al\", lastname: \"Pacino\"}); &gt; db.contacts.insert({firstname: \"Johnny\", lastname: \"Depp\"});<\/pre>\n<\/div>\n<p>And finally our endpoint should return these two records:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">curl http:\/\/localhost:3000\/contacts [{\"lastname\":\"Pacino\",\"firstname\":\"Al\",\"_id\":\"56158345fd2dabeddfb18799\"},{\"lastname\":\"Depp\",\"firstname\":\"Johnny\",\"_id\":\"56158355fd2dabeddfb1879a\"}]<\/pre>\n<p>Sweet! Again &#8211; in case of any problems, check <a href=\"https:\/\/github.com\/pjagielski\/modern-clj-web\/commit\/a4b858127299cfea1774c9efe4ffbab0e45df161\">this commit<\/a>.<\/p>\n<h2 id=\"getting-contacts-from-clojurescript\">Getting contacts from ClojureScript<\/h2>\n<p>In this step we\u2019ll fetch the contacts with AJAX call on our ClojureScript frontend. As usual, we need few dependencies in <code>project.clj<\/code> for a start:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">:dependencies ... [org.clojure\/clojurescript \"1.7.48\"] [org.clojure\/core.async \"0.1.346.0-17112a-alpha\"] [cljs-http \"0.1.37\"]<\/pre>\n<\/div>\n<p>ClojureScript should be already visible by using <code>figwheel<\/code>, but it\u2019s always better to require specific version explicitly. <code>cljs-http<\/code> is a HTTP client for ClojureScript and <code>core.async<\/code> provides facilities for asynchronous communication in CSP model, especially useful in ClojureScript. Let\u2019s see how it works in practice.<\/p>\n<p>To make an AJAX call we need to call methods from <code>cljs-http.client<\/code>, so let\u2019s add this in <code>core.cljs<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(ns ^:figwheel-always modern-clj-web.core (:require [cljs-http.client :as http])) (println (http\/get \"\/contacts\"))<\/pre>\n<p>You should see <code>#object[cljs.core.async.impl.channels.ManyToManyChannel]<\/code>. What madness is that???<\/p>\n<p>This is the time we enter the <code>core.async<\/code>. The most common way to deal with asynchronous network calls from Javascript is by using callbacks or promises. The <code>core.async<\/code> way is by using channels. It makes your code look more like a sequence of synchronous calls and it\u2019s easier to reason about. So the <code>http\/get<\/code> function returns a channel on which the result is published when response arrives. In order to receive that message we need to read from this channel by using <code>&lt;!<\/code> function. Since this is blocking, we also need to surround this call with <code>go<\/code> macro, just like in <a href=\"https:\/\/gobyexample.com\/goroutines\">go language<\/a>. So the correct way of getting contacts looks like this:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(:require ... [cljs.core.async :refer [ &gt;! chan]]) (go (let [response ( (http\/get \"\/contacts\"))] (println (:body response))))<\/pre>\n<h2 id=\"adding-om-component\">Adding Om component<\/h2>\n<p>Dealing with frontend code without introducing any structure could quickly become a real nightmare. In late 2015 we have basically two major JS frameworks on the field: Angular nad React. ClojureScript paradigms (functional programming, immutable data structures) fit really nice into React philosophy. In short, React application is composed of <em>components<\/em> taking data as input and rendering HTML as output. The output is not a real DOM, but so-called <em>virtual DOM<\/em>, which helps calculating diff from current view to updated one.<\/p>\n<p>Among many React wrappers in ClojureScript I like using <a href=\"https:\/\/github.com\/omcljs\/om\">Om<\/a> with <a href=\"https:\/\/github.com\/Prismatic\/om-tools\">om-tools<\/a> to reduce some verbosity. Let\u2019s introduce it into our <code>project.clj<\/code>:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">:dependencies ... [org.omcljs\/om \"0.9.0\"] [prismatic\/om-tools \"0.3.12\"]<\/pre>\n<\/div>\n<p>To render a \u201chello world\u201d component we need to add some code in <code>core.cljs<\/code>:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(:require ... [om.core :as om] [om-tools.core :refer-macros [defcomponent]] [om-tools.dom :as dom :include-macros true])) (def app-state (atom {:message \"hello from om\"})) (defcomponent app [data owner] (render [_] (dom\/div (:message data)))) (om\/root app app-state {:target (.getElementById js\/document \"main\")})<\/pre>\n<\/div>\n<p>What\u2019s going on here? The main concept of Om is keeping whole application state in one <em>global atom<\/em>, which is Clojure way of managing state. So we pass this <code>app-state<\/code> map (wrapped in <code>atom<\/code>) as a parameter to <code>om\/root<\/code> which mounts components into real DOM (<code>&lt;div id=\"main\"\/&gt;<\/code> from <code>index.html<\/code>). The <code>app<\/code> component just displays the <code>:message<\/code> value, so you should see \u201chello from om\u201d rendered. If you have <code>fighweel<\/code> running, you can change the message value, and it should be updated instantly.<\/p>\n<p>And finally let\u2019s render our contacts with Om:<\/p>\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">(defn get-contacts [] (go (let [response ( (http\/get \"\/contacts\"))] (:body response)))) (defcomponent contact-comp [contact _] (render [_] (dom\/li (str (:firstname contact) \" \" (:lastname contact))))) (defcomponent app [data _] (will-mount [_] (go (let [contacts ( (get-contacts))] (om\/update! data :contacts contacts)))) (render [_] (dom\/div (dom\/h2 (:message data)) (dom\/ul (om\/build-all contact-comp (:contacts data))))))<\/pre>\n<\/div>\n<p>So the <code>contact-comp<\/code> is just rendering a single contact. We use <code>om\/build-all<\/code> to render all contacts visible in <code>:contacts<\/code> field in global state. And most tricky part &#8211; we use <code>will-mount<\/code> lifecycle method to get contacts from server when <code>app<\/code> component is about to be mounted to the DOM.<\/p>\n<p>Again, in <a href=\"https:\/\/github.com\/pjagielski\/modern-clj-web\/commit\/321890e677fdcd71ee85d1391aa8dda990deaf41\">this commit<\/a> should be a working version in case of any problems.<\/p>\n<p>And if you liked Om, I highly recommend <a href=\"https:\/\/github.com\/omcljs\/om\/wiki\/Basic-Tutorial\">official tutorials<\/a> and <a href=\"https:\/\/blog.stephanbehnke.com\/zero-to-om\/\">Zero to Om<\/a> series.<\/p>\n","protected":false},"excerpt":{"rendered":"This is part 2 of my \u201cClojure web development\u201d series. You can discuss first part on this reddit&hellip;\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[491,687,71],"class_list":{"0":"post-12711","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-development-design","7":"tag-clojure","8":"tag-db","9":"tag-frontend"},"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/12711","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=12711"}],"version-history":[{"count":10,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/12711\/revisions"}],"predecessor-version":[{"id":15673,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/12711\/revisions\/15673"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=12711"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=12711"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=12711"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}