Contents


Write a Clojure web app on IBM Cloud

Delve into functional programming backed by the IBM Cloud infrastructure

Comments

Ready to dip your toes in the functional programming pool? Maybe Paul Graham's classic essays on Lisp convinced you to give it try. Maybe you want to learn a new way of doing things. Or maybe you want to see how much you can abstract your code and data.

And ideally, your web applications need the performance and high availability you get by running on IBM Cloud servers. In this tutorial, I show you how to use Node.js to run Clojure programs as part of an IBM Cloud web application.

What you'll need to build and deploy your Clojure application

  • An IBM Cloud account (register for your free trial account or log in to IBM Cloud if you already have an account).
  • Working knowledge of HTML and JavaScript.
  • Working knowledge of the MEAN application stack (at least Node.js and Express). If you're not familiar with it, you can learn the basics of the MEAN stack in my developerWorks series.
  • A development environment that supports uploading a Node.js application to IBM Cloud, such as Eclipse.

    Once you have an app running in IBM Cloud, you can quickly add a Git repository and continuous delivery pipeline to develop, test, and deploy it automatically. To set this up, on your app's Overview page, select Add Git Repo And Pipeline, and you can then do all of your development and deployment in your browser.

Hello, Clojure

Clojure is a dialect of Lisp that can be executed either on the JVM or in JavaScript. To run it from a Node.js application, use the clojurescript-nodejs package.

  1. Edit the packages.json file to add clojurescript-nodejs to the list of packages:
    "dependencies": {
        "express": "4.13.x",
    	"cfenv": "1.0.x",
    	"clojurescript-nodejs": "*"
    },
  2. Create a Clojure environment:
    // Get a Clojure environment
    var cljs = require('clojurescript-nodejs');
  3. Define a Clojure function (see the next section for a detailed description of this code):
    cljs.eval('(ns app (:require clojure.string)) ' +
    	'(defn h2 [str] (clojure.string/join ["<h2>" str "</h2>"]))');
  4. Respond to a URL request with Clojure code (see the next section for a detailed description of this code:
    app.get("/trivial", function(req, res) {
    res.send(cljs.eval('(ns app) (h2 "Hello, Clojure")'));
    });
  5. Run the application and see the result.

How does a Clojure function work?

If you already know Clojure, or even a different Lisp dialect, this material will be familiar to you.

In step 3 above, when you define a Clojure function, the cljs.eval function receives a string and evaluates it as Clojure code. In this case, the string is delimited by apostrophes ('), so that quotes (") can also be used.

cljs.eval('

The Clojure statement below does two things:

  1. Defines our name space as app. This is important because we cannot define variables, functions, etc. without putting them in a name space.
  2. Imports the clojure.string library. This library contains string manipulation functions, which are useful when generating HTML.
(ns app (:require clojure.string))

The Clojure statement below is the actual function definition. Because this is the first function we define, let's go over it in detail. The first part defines a function to be named h2.

(defn h2

The part below specifies that the function will have one parameter, called str. Notice that this part uses square brackets ([ ]) instead of typical ones. Clojure differs from traditional Lisp by using different bracket types to indicate different things. Square brackets define a vector, which acts as a list (a collection of items enclosed in regular parenthesis), but allows for faster access to the middle elements and faster insertion of items at the end.

	[str]

Next, the syntax to call a function from a library is <library name>/<function>. This part calls join from clojure.string. It is a function that receives a vector and returns a string with all the items. In this case, it encloses the string in h2 tags:

	(clojure.string/join ["<h2>" str "</h2>"])

The entire function definition is a list. The final parenthesis ends it.

)

In step 4 above, the Clojure code inside the app.get call is even simpler. It first identifies itself as part of the app namespace, and then calls the h2 function defined earlier. The cljs.eval function returns the result of the last expression, in this case the h2 call. This then is the value sent by res.send to the user.

res.send(cljs.eval('(ns app) (h2 "Hello, Clojure")'));

Writing the entire application in Clojure

For anything more sophisticated, it's easier to have the entire application in Clojure, with just a small stub in JavaScript.

JavaScript stub

Below is the vestigial app.js. The first line is an editor directive whose purpose is to avoid appearing to have an error:

/*eslint-env node*/

Next, create a Clojure environment:

// Get a Clojure environment
var cljs = require('clojurescript-nodejs');

Instead of evaluating a string, evaluate a file:

// Evaluate the application library
cljs.evalfile("app.cljs");

Clojure application

The first part of app.cljs is equivalent to the app.js that comes with the template. Here it is, explained line by line.

These lines define the name space (my-ns) and import the two libraries we need: clojure.string and cljs.nodejs. The first is just imported, but the second is enclosed in a vector with :as node to specify that the code in the rest of the name space refers to the package as node.

(ns my-ns (:require clojure.string
			[cljs.nodejs :as node]
	   )
)

Clojure comments start with a semicolon (;).

; Get the application environment

This line shows two of the mechanisms you use in Clojure to communicate with the underlying JavaScript code. The js/ syntax is used to access JavaScript globals, such as require. The snippet (js/require "cfenv") is equivalent to require("cfenv"). To call a method of an object in Clojure, the syntax is (.<method> <object> <parameters, as needed>). The snippet (.getAppEnv (js/require "cfenv")) is equivalent to require("cfenv").getAppEnv().

Finally, def defines a global variable. It calculates the value of the second parameter, and assigns it to the first one. This line overall is equivalent to var appEnv = require("cfenv").getAppEnv():

(def appEnv (.getAppEnv (js/require "cfenv")))

This line is similar to the appEnv line, except without the method call:

; Create an express application
(def express (js/require "express"))

This line shows another way to access JavaScript: (js* <expression>) runs that JavaScript expression, and returns the value. This line is equivalent to var app = require("express")():

(def app (js* "require('express')()"))

This line shows another mechanism to use JavaScript. When you have an object, the syntax (aget <object> <attribute>) gets an attribute of the object. Combined with the (.<method> <object> <parameters, as needed>) syntax, this entire line means app.use(express.static("public")), which is the code that specifies to serve that static files in the public directory.

; Static files
(.use app ((aget express "static") "public"))

This is an example of code that specifies the response to a GET request for the path /trivial. The syntax to create an anonymous function is (fn [<parameters>] (<expression>)). Combining this with the way JavaScript methods are called, this is equivalent to app.get("/trivial", function(req, res) {res.send("hello")}):

; Respond to a request
(.get app "/trivial" (fn [req res] (.send res "Hello")))

This is equivalent to the code that starts the application listening on the port specified in appEnv in JavaScript:

; Start listening
(.listen app
	(aget appEnv "port")
	"0.0.0.0"
	(fn []
		(.log js/console (aget appEnv "url"))
	)
)

Responding to the user

A web application that returns a fixed response regardless of user input is not very useful. Real web applications have to process user input. This input typically comes in three forms: in the path, in a query, or in the body of the HTTP request.

Path parameters

In Express (the Node.js HTTP server library), you denote path parameters by specifying :<keyword> as a path component. The result is then provided in req.params. Below is the relevant code.

First, create a string with a link back to the forms page. This is just cosmetic, to make it easy for users to go back.

; Go back to the form
(def goBackForm "<hr /><a href=\"/form.html\">Go back</a>");

This is the actual call that receives all the GET requests for /process-path/<whatever>:

; Process a part of the path
(.get app "/process-path/:param"

This is the function called when the URL is requested. It calls res.send with a concatenation of two strings: the parameter value, and the HTML for a link going back to the form page (defined earlier as goBackForm).

	(fn [req res]
		(.send res
			(clojure.string/join [

Remember that (*js <expression>) evaluates the expression as JavaScript, and returns the value to Clojure. This makes it easy to read a JavaScript variable, such as req.params.param.

				(js* "req.params.param")
				goBackForm
			])
		)
	)
)

Query parameters

Query parameters are even easier to handle. There is no need to specify the parameters' names with a colon; they are already created by the browser from the HTML.

; Respond to GET requests with a query
(.get app "/process-form"
	(fn [req res]
		(.send res
			(clojure.string/join [
				(js* "req.query.get")
				goBackForm
			])
		)
	)
)

Body parameters

Parameters in the body (used by the POST and PUT methods) require a bit more processing. First, you need to add process-body to the packages.json file. Then, you need to put it as middleware to process the body entries when they are encoded. After that step, the actual parameters are accessible the same way they are accessible for other methods.

; Parse the body
(.post app "*"

The JavaScript expression returns a function. Functions are valid as a return value and as a function parameter in both JavaScript and Clojure.

	(js* "require('body-parser').urlencoded({extended: true})")
)

; Respond to POST requests
(.post app "/process-form"
	(fn [req res]
		(.send res
			(clojure.string/join [
				(js* "req.body.post")
				goBackForm
			])
		)
	)
)

Putting all the parameters together

Usually you don't care where parameters came from; you only care about their values. Because parameters have a key and a value, the appropriate data structure to use is a map.

Below is an example of code that does this. The first part is a function definition. Because this is a named function, one that can be called from other places, it is defined with defn and not fn.

; Given a request object, return a map with all the parameters
(defn getParams [req]

The let call defines a number of local variables (roughly speaking, they are actually called symbols in functional programming) that can be used in the expression or expressions at its end. In this case, it defines three of them: queryMap, bodyMap, and paramMap.

	(let [    ; Define local variables

All three definitions use the js->clj function to translate from a JavaScript object to a Clojure map.

		queryMap (js->clj (js* "req.query"))
		bodyMap (js->clj (js* "req.body"))
		paramMap (js->clj (js* "req.params"))		
	]

The merge function takes the three different maps and merges them into a single map (values in the later maps override values in the earlier maps if the keys are the same). This expression is the result of the let, and therefore of the entire function.

	(merge queryMap bodyMap paramMap)
	)   ; end of let
) ; end of defn

This is the call that actually responds to requests:

; Respond requests with parameters from all over
(.all app "/merge/:pathparam"
	(fn [req res]

Here's another use of let. Coming from an imperative programming background, I find it easier to write and understand functions when most of the processing is done by defining symbols that I then use.

		(let [ ; local variables

Call the function we defined previously to get the parameters.

				params (getParams req)
			]
			(.send res
				(clojure.string/join [

Now, we want to use JSON.stringify to turn the map into JSON. For this purpose, use clj->js to convert the map back to a JavaScript object:

					(.stringify js/JSON (clj->js params))
					goBackForm
					])  ; end of clojure.string/join
			)  ; end of res.send
		)	; end of let
	) ; end of fn [req res]
)   ; end of app.all

Cheat sheet for other APIs

The same tools used for the Node.js and Express packages can be used for any other API or package.

  • To use a package, use (def <var name> (js/require "<package name>")).
  • To call a method, use (.<method> <object> <parameters, if any>).
  • To use a global variable from JavaScript, use js/<variable>.
  • If all else fails, use this syntax to evaluate a JavaScript expression: (js* "<JavaScript expression>").

Conclusion

This tutorial has given you the basics for writing web applications in Clojure. Your next step is to learn how to actually use functional programming features to make your applications better. There are a number of books on the subject; my favorite is The Joy of Clojure, by Michael Fogus and Chris Houser.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Cloud computing, Web development
ArticleID=1035659
ArticleTitle=Write a Clojure web app on IBM Cloud
publish-date=07262017