Upon discovering Node.js, the first shiny demo one typically comes across is a toy http server. It's even prominently featured on Node's "About" page. Following that, one inevitably finds Express.
We'll be showcasing the same today, but instead of JavaScript, we'll be porting the Express "Hello world" example to ClojureScript, building on a previous CLJS/Node blog post (so if you haven't read it, start there and come back when you're done).
Note: this post assumes you've followed the update at the end of the last post and switched your compilation :optimizations
to :simple
, as this makes running the final build artifacts easier.
Getting to "Hello world!" (again)
We'll be doing the following:
- Clean up unused files from last tutorial
- Add
express
NPM dependency - Modify
pow.core
- Build and run!
Simple enough!
Clean up unused files from last tutorial
We repurposed the mies
lein template for our last tutorial. While this gets us up and running, there are a few files included in there that we don't need right now. Let's just remove them quickly.
$ cd path/to/pow
$ rm index.html
$ rm index_release.html
Add express
npm dependency
As of writing, the latest version of express
in npm is 4.11.1
. Our project.clj
already includes a :node-dependencies
entry; just add express to that existing vector:
;...
:node-dependencies [[source-map-support "0.2.8"]
[express "4.11.1"]]
;...
Pull down your new dependency:
$ lein npm install
Your project should now have a node_modules
directory containing both express
and source-map-support
.
Modify pow.core
A word of warning in advance: Clojure and ClojureScript eschew wanton state modification whenever possible, encouraging you to use pure functions and value semantics. However, we're using a JavaScript library written without such lofty goals. Consequently, this code will not be seen as idiomatic ClojureScript. It's "functional" only in the sense that it functions, relying on hidden internal state (and changes to that state via method calls with side-effects) in order to work.
While this may seem to defeat the purpose of using ClojureScript, it underlines an important point: ClojureScript does not strong-arm you into using its conventions. Certainly, every single one of its built-in data structures and core methods will encourage this, but at the end of the day, you can drop down to what is effectively transliterated JavaScript to get the job done.
Edit src/server/pow/core.cljs
thusly:
(ns pow.core
(:require [cljs.nodejs :as node]))
(node/enable-util-print!)
(def express (node/require "express"))
(defn say-hello! [req res]
(.send res "Hello world!"))
(defn -main []
(let [app (express)]
(.get app "/" say-hello!)
(.listen app 3000 (fn []
(println "Server started on port 3000")))))
(set! *main-cli-fn* -main)
That's all there is. Let's attack this line-by-line.
(ns pow.core
(:require [cljs.nodejs :as node]))
This declares your current namespace, pow.core
, and requires cljs.nodejs
into your current scope as node
. cljs.nodejs
defines namespaced references to Node's process
and require
globals, and defines a shortcut for delegating ClojureScript's print to Node's util.print
, which we call on the next line:
(node/enable-util-print!)
Following that is a call to cljs.nodejs/require
which pulls in express
:
(def express (node/require "express"))
Exactly how you decide to make use of node modules will depend on the modules themselves. In this case, the express
npm package exports a factory function which can be called to create new app instances. Others might export a constructor, requiring that you make new instances of it. In that case, you may choose to to create your own factory function which hides this fact, such as:
(defn make-bob []
(let [bob (node/require "bob-mod")]
(bob.)))
You could then (make-bob)
to your heart's content. It may seem wasteful to require the module for every call, but Node caches the result of the initial module evaluation, so subsequent calls are cheap.
Moving on!
(defn say-hello! [req res]
(.send res "Hello world!"))
Here's the crux of the program. This defines our handler, say-hello!
. We bind this to the root ("/") route in -main
:
;...
(.get app "/" say-hello!)
;...
The follwing line in -main
starts up the server, printing a message when it's up an running:
(.listen app 3000 (fn []
(println "Server started on port 3000")))
Build and run!
From the CLI, build and run your app:
$ lein cljsbuild once server
Compiling ClojureScript.
Compiling "out/server/pow.js" from ["src/server"]...
Successfully compiled "out/server/pow.js" in 14.621 seconds.
$ node out/server/pow.js
Server started on port 3000
Crack open your browser and visit http://localhost:3000/.
END
So easy! Next, we'll take a look at the ONE-CC Stack: Om, Node, Express, ClojureScript, Cassandra. Yes, I just made that up.