Most of your app's configuration is stored in the resources/config.edn
file:
{:biff/base-url #profile {:prod #join ["https://" #biff/env DOMAIN]
:default "http://localhost:8080"}
:biff/host #profile {:dev "0.0.0.0"
:default "localhost"}
:biff/port 8080
:biff.xtdb/dir "storage/xtdb"
:biff.xtdb/topology #keyword #or [#profile {:prod #biff/env "PROD_XTDB_TOPOLOGY"
:default #biff/env "XTDB_TOPOLOGY"}
"standalone"]
:biff.xtdb.jdbc/jdbcUrl #biff/secret "XTDB_JDBC_URL"
...
This file is parsed with Aero. The biff/use-aero-config
component sets the profile to the value of the BIFF_PROFILE
environment variable. In production, BIFF_PROFILE
is set to prod
; during development, it's set to dev
. You can add new profiles if needed, like a :ci
profile for running automated tests.
biff/use-aero-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}]
[:html
[:body
[:p "This website is located at " base-url]]])
(def module
{:routes [["/hello" {:get hello}]]})
Configuration is only read by biff/use-aero-config
during app startup, so if you modify resources/config.edn
or config.env
during development, you'll need to call com.example/refresh
for the changes to take effect.
Secrets should always be kept in the config.env
file, which isn't checked into git:
MAILERSEND_API_KEY=abc123
...
The biff/use-aero-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:
# config.env
MAILERSEND_API_KEY=abc123
;; config.edn
{:mailersend/api-key #biff/secret MAILERSEND_API_KEY ...
then the following handler would print abc123
to the console:
(defn hello [{:keys [biff/secret] :as ctx}]
(println (secret :mailersend/api-key))
...)
(def module
{:routes [["/hello" {:get hello}]]})
This is done so that your secrets won't be exposed if you serialize your system map (e.g. by printing it to your logs).
If you need to provide different values for a secret in different environments, you can specify separate environment variable names in resources/config.edn
:
:stripe/api-key #profile {:prod "STRIPE_API_KEY_PROD"
:dev "STRIPE_API_KEY_TEST"}
If you need to store your config and/or secrets in some other way, you can replace biff/use-aero-config
with a custom component:
(defn use-custom-config [system]
(let [config ...]
(merge system config)))
(def components
[use-custom-config
biff/use-xtdb
...])
use-custom-config
can load configuration in whatever way you'd like it to, as long as it satisfies two conditions:
:biff/secret
is set to a 1-arg function that takes a keyword and returns the associated secret value.See also: