Most of your app's configuration is stored in the config.edn file:

{:prod {:biff/base-url ""
        :biff.xtdb/dir "storage/xtdb"
        :biff.xtdb/topology :standalone
        :postmark/api-key "POSTMARK_API_KEY"
        :postmark/from ""
 :dev {:merge [:prod]
       :biff/base-url "http://localhost:8080"
       :biff.middleware/secure false
 :tasks {:biff.tasks/deploy-cmd ["git" "push" "prod" "master"]
         :biff.tasks/server ""

The biff/use-config component checks the BIFF_ENV environment variable to know which section of the config file should be used. In production, BIFF_ENV is set to prod; during development, it's set to dev. You can add new sections if needed, like a :ci section for running automated tests. You can add :merge [:prod] or similar to a config section to make it inherit values from other sections.

The :tasks section isn't used by biff/use-config; it's instead read by bb tasks.

biff/use-config merges your config into the system map. Since the system map is in turn merged with incoming requests, you can read config values in your request handlers like so:

(defn hello [{:keys [biff/base-url] :as ctx}]
    [:p "This website is located at " base-url]]])

(def plugin
  {:routes [["/hello" {:get hello}]]})

Configuration is only read by biff/use-config during app startup, so if you modify the config.edn file during development, you'll need to call com.example/refresh for the changes to take effect.


Secrets are stored in the secrets.env file, which is used to populate environment variables before starting your app:

export POSTMARK_API_KEY=abc123

The biff/use-secrets component sets the :biff/secret key in the system map to a function. That function takes a keyword and returns the associated secret. For example, if your config and secrets files have the following contents:

# secrets.env
export POSTMARK_API_KEY=abc123

;; config.edn
{:prod {:postmark/api-key "POSTMARK_API_KEY"

then the following handler would print abc123 to the console:

(defn hello [{:keys [biff/secret] :as ctx}]
  (println (secret :postmark/api-key))

(def plugin
  {:routes [["/hello" {:get hello}]]})

In other words: the :biff/secret function first looks up the value of the given keyword in your configuration, which should be set to the name of an environment variable. Then the :biff/secret function returns the value of that environment variable.

If you need to provide different values for a secret in different environmentns, you can specify separate environment variable names in config.edn:

{:prod {:stripe/api-key "STRIPE_API_KEY_PROD"
 :dev {:stripe/api-key "STRIPE_API_KEY_TEST"

Version control

config.edn isn't checked into git by default, but it's safe to do so as long as you remember to store all your secrets in secrets.env instead of config.edn. Leaving config.edn out of source control is helpful if you're developing an open-source app, so other users can supply their own configuration files.

secrets.env should not be checked into git.

Have a question? Join the forum or the #biff channel on Clojurians Slack.

Sign up for Biff: The Newsletter
Announcements, blog posts, et cetera et cetera.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.