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: