Setup-for-development
There are different options to optimize your development workflow:
- Reload namespaces on source file changes
- Setup with lein-ring plugin
- Setup with boot
- Use a module lifecycle library [[Interactive Development]]
The first approach is fast, simple to setup. The ring-devel library provides middleware for this purpose.
Downside, namespace reloading does not reload application state. Consequently, there can be situation requiring to restart the server.
Setup using the lein-ring plugin
The Lein-Ring plugin is the most straightforward way. Create two files
- src/sample.clj
- project.clj
A simple service returning 'Hello World' in src/sample.clj
(ns sample)
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello world."})
Add lein-ring and configure the ring handler to your project file, project.clj:
:ring {:handler sample/handler}
A minimal project.clj:
(defproject lein-demo "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.8.0"]
[ring/ring-core "1.6.3"]
[ring/ring-jetty-adapter "1.6.3"]
[ring/ring-devel "1.6.3"]]
:ring {:handler sample/handler}
:plugins [[lein-ring "0.12.5"]])
Start a development server:
lein ring server
Visit the server at http://localhost:3000
The server will automatically reload any modified files in your source directory.
Setup using Clojure deps and CLI
Clojure provides command line tools for transitive dependency graph expansion and the creation of classpaths. Visit https://clojure.org/guides/deps_and_cli for more information.
Create the following project file structure:
.
├── deps.edn
├── dev
│ └── hotreload.clj
└── src
└── sample
└── server.clj
Describe the dependencies and classpaths.
;; deps.edn
{:paths ["src"]
:deps
{org.clojure/clojure {:mvn/version "1.9.0"}
ring/ring-core {:mvn/version "1.6.3"}
ring/ring-jetty-adapter {:mvn/version "1.6.3"}}
:aliases
{:dev
{:extra-paths ["dev"]
:extra-deps {ring/ring-devel {:mvn/version "1.6.3"}}
:main-opts ["-m" "hotreload"]}}}
Create the production server.
;; src/sample/server.clj
(ns sample.server
(:require [ring.adapter.jetty :refer [run-jetty]])
(:gen-class))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain; charset=UTF-8"}
:body "hello world!\n"})
(defn -main [& args]
(run-jetty handler {:port 3000}))
Start the server with clojure -M -m sample.server
. Visit
http://localhost:3000 and confirm the string "hello world!" displays.
Create the development server.
;; dev/hotreload.clj
(ns hotreload
(:require [ring.adapter.jetty :refer [run-jetty]]
[ring.middleware.reload :refer [wrap-reload]]
[sample.server :refer [handler]])
(:gen-class))
(def dev-handler
(wrap-reload #'handler))
(defn -main [& args]
(run-jetty dev-handler {:port 13000}))
Start the server with clojure -A:dev -M:dev
. Visit
http://localhost:13000 and confirm the string "hello world!" displays.
Note, newer Clojure CLI versions do to need the extra -A:dev
part.
The server will automatically reload any modified files in your source directory.
Setup using boot
Boot is a build tool for Clojure', https://boot-clj.github.io/
Create two files
- build.boot
- src/sample.clj
A minimal build.boot
(set-env!
:resource-paths #{"src"}
:dependencies '[[org.clojure/clojure "1.8.0"]
[ring/ring-core "1.6.3"]
[ring/ring-jetty-adapter "1.6.3"]
[ring/ring-devel "1.6.3"]])
(deftask dev
"Run server hot reloading Clojure namespaces"
[p port PORT int "Server port (default 3000)"]
(require '[sample :as app])
(apply (resolve 'app/run-dev-server) [(or port 3000)]))
A simple service returning 'Hello World' without reloading in src/sample.clj
(ns sample
(:require
[ring.adapter.jetty :refer [run-jetty]]))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello world"})
(defn run-dev-server
[port]
(run-jetty handler {:port port}))
Inspect your boot task
boot dev -h
Run the server
boot dev
Visit http://localhost:3000
Next, we can add hot reloading. Check that the dependencies in build.boot include ring/ring-devel. Wrap the handler into a wrap-reload handler.
(ns sample
(:require
[ring.adapter.jetty :refer [run-jetty]]
[ring.middleware.reload :refer [wrap-reload]]))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello world"})
(def dev-handler
(wrap-reload #'handler))
(defn run-dev-server
[port]
(run-jetty dev-handler {:port port}))
Restart the server and visit http://localhost:3000
Now, the server will automatically reload any modified files in your source directory.