Quickie: Using figwheel and leiningen as a proxy server for fun and profit

Overview:

Leiningen is an awesome part of the Clojurescript landscape, doing live reloading of code and CSS. It even offers a static-file server, if you happen to be developing a client-only application.

Less appreciated is that you can plug in your own Ring handler. A particularly convenient use-case that I’ve recently discovered is to plug in a proxy server, which has uses beyond client-only applications.

There’s three main use-cases justifying it for me:

  • We frequently use a Django API backend so it would normally make sense to use the Django development server, but on Windows this is unbearably slow (with the number of files generated by Clojurescript in dev-mode). Our previous solution involved nginx, which works but is a more complicated set-up.
  • If you are using a third-party service that hasn’t enabled CORS yet, you can proxy to it from your local server instead.
  • If you are still doing client-only development, but need to assume an AJAX service will be available on the eventual host, you can still refer to it using server-less URL paths and simply proxy to the test service.

How-To:

There is a sample project in the leiningen repo, but essentially all you need to do is define your handler, and specify it in the :ring-handler entry of your :figwheel configuration in project.clj .

You have a few options for Ring proxy handlers, but the one I went with is this one: tailrecursion/ring-proxy.

To use it, grab the leiningen sample and extend it for your needs:

(ns com.etbwc.dev-handlers
  (:require [compojure.core :refer [defroutes GET]]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [ring.util.response :as response]
            [tailrecursion.ring-proxy :refer [wrap-proxy]]))

(defroutes app-routes
  ;; NOTE: this will deliver all of your assets from the public directory
  ;; of resources i.e. resources/public
  (route/resources "/" {:root "public"})
  ;; NOTE: this will deliver your index.html
  #(GET "/" [] (-> (response/resource-response "index.html" {:root "public"})
                  (response/content-type "text/html")))
  (route/not-found "Not Found"))


(def app (-> app-routes
             (wrap-defaults site-defaults)
             ;; Use a django API on a different localhost port:
             (wrap-proxy "/api" "http://localhost:8000/api")
             ;; Use the django static-files if you need for eg the admin site:
             (wrap-proxy "/static" "http://localhost:8000/static")
             ;; Proxy requests to a third-party API without CORS enabled:
             (wrap-proxy "/pq" "http://third.party.com/pq")))

Now to use the handler simply mention it in project.clj:

(defproject "etbwc-example" "0.1.0-SNAPSHOT"
  ;;...

  :figwheel {:http-server-root "public"
             :server-port      3451
             :nrepl-port       7892
             ;; Boom:
             :ring-handler     com.etbwc.dev-handlers/app
             :css-dirs         ["resources/public/css"]}

  ;;...
  )

Clojure

390 Words

2017-06-12 00:00 +0000

comments powered by Disqus