deploy compojure app that is also a CLI app

378 views Asked by At

I have a Clojure app that I can use both from the command-line, and as a Compojure app. I did that by putting a ring handler and a main function (clojure.tools.cli) in my leiningen project.clj.

{... :main my-app.core :ring {:handler my-app.handler/handler }}

The handler is defined (defroutes handler ...).

Now if I want to run the CLI app, I can run lein uberjar and then java -jar arguments. And I can also run the Compojure app via lein ring server PORT.

Now, how do I deploy the thing as a Compojure app (and not a CLI app) in a production server ? (Bonus points for explaining how lein ring server works.)

Note : I already use nginx if that can help, and I'm flexible on the container to be used.

2

There are 2 answers

2
Dax Fohl On BEST ANSWER

Here's kind of the default template I use for new projects. It allows you to do dependency injection into ring apps and run the app from the command line as an uberjar. You can read more here: http://www.sparxeng.com/blog/software/improved-clojure-workflow

; handler.clj    
(defn wrap-inject-deps
  "Ring middleware that injects the dependencies into each ring request map"
  [handler deps]
  (fn [req]
    (handler (assoc req :deps deps))))

(defn create-handler
  "Similar to the default ring 'handler', but with a parameter to let you inject dependencies"
  [deps]
  (-> (routes
        api-routes
        web-routes
        (route/resources "/"))
      (kwp/wrap-keyword-params)
      (params/wrap-params)
      (json/wrap-json-params)
      (json/wrap-json-response)
      (wrap-inject-deps deps))) ; this injects dependencies

(defn start-jetty
  "Launch the Jetty instance"
  [deps]
  (let [port (Integer. (or (-> system :env :port) 5000))
        handler (create-handler deps)]
    (jetty/run-jetty handler {:port port :join? false})))


; system.clj
(defn get-env
  "Initialize configuration vars"
  []
  {:aws-access-key-id     (System/getenv "AWS_ACCESS_KEY_ID")
   :aws-secret-access-key (System/getenv "AWS_SECRET_ACCESS_KEY")
   :mongo-url             (System/getenv "MONGO_URL"))

(defn start
  "Launch dependencies such as DB connection and Jetty. Return these as a map, for use in REPL"
  [& [env]]
  (let [env (or env (get-env))
        deps {:env     env
              :monger  (db/init env)}
        jetty (handler/start-jetty deps)]
    (assoc deps :jetty jetty)))


; program.clj
(defn -main [& [port]]
  "App entrypoint"
  (let [env (system/get-env) ; "env" is just a map of config variables, that can be hard-coded, read from file, or whatever you want.
        env (if port (assoc env :port port) env)]
    (system/start env)))

You can then use leiningen profiles if you need to create multiple apps with different entrypoints from your codebase.

7
schaueho On

lein ring server fires up jetty to serve the web application you've built. It does this by starting the server with the options you specify in :ring. If you specify LEIN_NO_DEV in your environment, the server will run in production mode. Whether jetty is the right fit for your productions needs or not, is up to you to decide.

If you would like to run your web application on a different server, there are multiple options. One of them is running on Tomcat. Take another look at the documentation for lein ring, in particular the section on war files. Basically, you would call lein war or lein uberwar to generate a war file. You can then e.g. drop the war file into the webapps directory of Tomcat.

Another option would be using immutant, which has substantial documentation on installation and running applications.