Setting up Heroku config variables with Clojure
03 Sep 2015Configuration or environment variables are really useful whether you want to externally affect the way your apps run or if you simply want to keep private, sensitive data out of your version control system. The other day I ran into some trouble having Heroku read the config variables of a Clojure app of mine. So here's how to get it working.
1. Add the variables to Heroku
In your Heroku app dashboard, go into settings and click "Reveal Config Vars". If you haven't added any configuration variables yet, it should look something like this:
Go ahead, then, and click "Edit" to add the configuration variables you want to add. The next image shows what you should now be seeing. Type in the key and the value for each one.
2. Reference your variables in your Clojure code
Now for the trickier part. You might be tempted to simply write something like the following (which was actually what I was doing):
(def MY-VAR (System/getenv "MY-VAR"))
However, as per Heroku's documentation, only "at runtime, config vars are exposed as environment variables to the application", and since def
calls are bound at compile time, the above code would not work.
So, what's the solution?
Well, the solution is to bind your variables at runtime, inside a function call which executes when the app is started.
- But you shouldn't declare vars inside functions, or change them using def
calls! - the attentive reader argues; correctly. The code that follows doesn't do either.
;;;;;;;;;;;;;;
;; option A ;;
;;;;;;;;;;;;;;
;; 1) declare a nil atom
(def MY-VAR (atom nil))
;; 2) "reset!" it in a function that is called when the app starts
(defn init-config []
(reset! MY-VAR (System/getenv "MY-VAR")))
;;;;;;;;;;;;;;
;; option B ;;
;;;;;;;;;;;;;;
;; 1. declare an unbound var (can also be bound,
;; but its value will change)
(def MY-VAR)
;; 2. "alter-var-root" it in a function which is
;; called when the app starts
(defn init-config []
(alter-var-root #'MY-VAR (constantly (System/getenv "MY-VAR"))))
Either option will work in Heroku, as long as reading the environment is done at runtime. A third alternative can be accomplished by using the Environ library. It allows for a more programatic approach and encompasses a handful of other features that you might find useful. Refer to its GitHub page for more information.
If you liked this post or have any questions / suggestions, be sure to drop me a line or reach me on Twitter. Happy hacking!