5. A REST Client in ClojureScript

It’s a common pattern to write client-server applications with a JavaScript client. The inventor of Clojure, Rich Hickey, said at the announcement of ClojureScript, “Clojure rocks, JavaScript reaches.”1 He meant that it is great to be able to write Clojure that runs on the server JVM and the client browser in JavaScript. We’re going to work through a pattern for doing this.

1. ClojureScript Announcement, July 2011, https://www.youtube.com/watch?v=tVooR-dF_Ag

In this recipe we will create a client in ClojureScript that will make a REST call. Then we will reuse the REST server from the previous chapter to receive the call. We will see the entire process end to end.

Assumptions

In this chapter we assume the following:

Image You know about ClojureScript and how Clojure can be compiled into JavaScript.

Image You understand that you can make calls to RESTful services from JavaScript.

Image You understand basic command prompt operations, such as renaming files.

Image You understand that that we can include JavaScript inside HTML files in order to run them.

Image You have installed Chrome as a web browser.

Benefits

You’ll get to consume a REST web service client-side in a browser, using ClojureScript. You’ll see ClojureScript compiled to JavaScript, and see it running. You’ll see how ClojureScript can interact with other JavaScript libraries like the Google Closure JavaScript libraries.

The Recipe—Code

Follow these steps:

1. Create a minimal ClojureScript application with

lein new mies cljs-rest-client-server-demo

2. Ensure that the project.clj file contains the following:

(defproject cljs-rest-client-server-demo "0.1.0-SNAPSHOT"

  :dependencies [[org.clojure/clojure "1.7.0-beta2"]
                 [org.clojure/clojurescript "0.0-3308"]
                 [ring/ring-core "1.4.0-RC1"]
                 [ring/ring-json "0.3.1"]]

  :node-dependencies [[source-map-support "0.2.8"]]

  :plugins [[lein-cljsbuild "1.0.6"]
            [lein-ring "0.9.5"]]

  :ring {:handler  cljs-rest-client-server-demo.handler/app}

  :source-paths ["src" "target/classes"]

  :clean-targets ["out/cljs_rest_client_server_demo"
"cljs_rest_client_server_demo.js"
"cljs_rest_client_server_demo.min.js"]

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src-cljs"]
              :compiler {
                :main cljs-rest-client-server-demo.core
                :output-to "out/cljs_rest_client_server_demo.js"
                :output-dir "out"
                :optimizations :none
                :cache-analysis true
                :source-map true}}
             {:id "release"
              :source-paths ["src-cljs"]
              :compiler {
                :main cljs-rest-client-server-demo.core
                :output-to "out-adv/cljs_rest_client_server_demo.min.js"
                :output-dir "out-adv"
                :optimizations :advanced
                :pretty-print false}}]})

3. Rename the directory src to src-cljs.

4. Modify the file src-cljs/cljs_rest_client_server_demo/core.cljs and ensure it contains the following:

(ns cljs-rest-client-server-demo.core
  (:require [clojure.browser.repl :as repl]
            [goog.net.XhrIo :as xhr]
            [goog.Uri.QueryData :as query-data]
            [goog.structs :as structs]
            [goog.dom :as dom]))

(enable-console-print!)

(defn receiver [event]
  (let [response (.-target event)]
    (println (.getResponseText response))
    (set! (.-value (dom/getElement "returnVal"))
         (.getResponseText response))))

(defn post [url content]
  (xhr/send url receiver "POST" content))

(defn ^:export main [& _]
  (println "starting!")
  (post "/4459"
         (query-data/createFromMap
      (structs/Map. (clj->js {:mykey
        (.-value (dom/getElement "returnVal"))}))))
  (println "done"))

5. In the project directory, run the following command:

lein cljsbuild once

6. Next create the subdirectories resources/public/ under the project directory.

7. Copy the file index.html to the resources/public directory.

8. Then copy the directory out to the resources/public directory.

9. Create the subdirectory src/cljs_rest_client_server_demo under the project directory.

10. Create the file src/cljs_rest_client_server_demo/handler.clj and ensure it contains the following:

(ns cljs-rest-client-server-demo.handler
  (:require [ring.util.response :as ring-res]
            [compojure.route :as route]
            [compojure.handler :as handler]
            [ring.middleware.resource :as resources]
            [ring.middleware.params :as params])
  (:use compojure.core
        ring.middleware.file-info))

(defroutes app-routes
  (PUT    "/:id"  {params :params}
    (str "put called with params: "  params))
  (POST    "/:id"  {params :params}
    (str "post called with params: "  params))
  (route/resources "/")
  (route/not-found
    "<a href='/index.html'>Try it</a>"))

(def app
  (-> app-routes
      (params/wrap-params)
      handler/api
      (resources/wrap-resource "public")
      (wrap-file-info)))

11. Modify the file resources/public/index.html to contain the following:

<html>
    <body>
        <script src="out/cljs_rest_client_server_demo.js"
type="text/javascript"></script>
        <button onclick="cljs_rest_client_server_demo.core.main()">Try it
</button>
        Return Value: <input type="text" id="returnVal"><br>
    </body>
</html>

Testing the Solution

To test the solution, follow these steps:

1. In the command prompt in the project directory, start the REST and web server with ring on port 4000:

lein ring server 4000

This should open a new web browser window with the following URL:

http://localhost:4000/index.html

2. Right-click on the page in Chrome and select Inspect element.

3. Select the tab Console.

4. Click the Try it button on the web page.

5. Observe the following entry in the console:

starting!
done
post called with params: {:id "4459", :mykey "42"}

Notes on the Recipe

In the project.clj file, note in particular the :source-paths parameter. It’s looking for a directory called src for Clojure and src-cljs for ClojureScript. This is the prompt to the Clojure and ClojureScript compilers to tell them where to find the ClojureScript files.

  :source-paths ["src" "target/classes"]
...
    :builds [{:id "dev"
              :source-paths ["src-cljs"]
...
             {:id "release"
              :source-paths ["src-cljs"]

Note in the file src/core.cljs that we use the line (xhr/send url receiver "POST" content) to send a post request to the server using Google’s Closure library.

(defn post [url content]
  (xhr/send url receiver "POST" content))

Then we send debugging information to the JavaScript in the console using the following line (println "starting!"). You can view this in your Chrome browser by right-clicking on a page, selecting Inspect Element, and then clicking the Console tab. Note that we enabled (println in ClojureScript using the expression (enable-console-print!). Without this, we would have had to use (.log js/console "starting!").

(defn ^:export main [& _]
  (println "starting!")
  (post "/4459"
        (query-data/createFromMap
      (structs/Map. (clj->js {:mykey
        (.-value (dom/getElement "returnVal"))}))))
  (println "done"))

In the file src/cljs_rest_client_server_demo/handler.clj we write our server. Note in particular the line above wrap-file-info. This is middleware to ensure that mime types are set correctly when files are requested.

(def app
  (-> app-routes
      (params/wrap-params)
      handler/api
      (resources/wrap-resource "public")
      (wrap-file-info)))

Conclusion

In this chapter we have done the following:

Image Built a simple REST server to respond to our requests.

Image Built a ClojureScript client to call our REST server.

Image Kicked off the ClojureScript client from an HTML page loaded into a web browser.

Image Observed the result of the client calling the server in the console of the browser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.212.217