{"id":15046131,"url":"https://github.com/logicblocks/configurati","last_synced_at":"2025-10-26T02:31:20.987Z","repository":{"id":47745163,"uuid":"118943001","full_name":"logicblocks/configurati","owner":"logicblocks","description":"Clojure library for managing application configuration.","archived":false,"fork":false,"pushed_at":"2024-10-28T00:07:28.000Z","size":335,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-10-30T03:22:48.145Z","etag":null,"topics":["application-config","application-configuration","clojure","clojure-library","configuration","configuration-file","configuration-files","env","environment-variables","yaml"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/logicblocks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-25T17:09:49.000Z","updated_at":"2024-10-28T00:07:26.000Z","dependencies_parsed_at":"2024-12-04T01:35:05.100Z","dependency_job_id":null,"html_url":"https://github.com/logicblocks/configurati","commit_stats":{"total_commits":114,"total_committers":5,"mean_commits":22.8,"dds":"0.20175438596491224","last_synced_commit":"63f622c60166ee01026d0491ac8ad765a7db1dfd"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logicblocks%2Fconfigurati","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logicblocks%2Fconfigurati/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logicblocks%2Fconfigurati/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logicblocks%2Fconfigurati/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/logicblocks","download_url":"https://codeload.github.com/logicblocks/configurati/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238247984,"owners_count":19440879,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-config","application-configuration","clojure","clojure-library","configuration","configuration-file","configuration-files","env","environment-variables","yaml"],"created_at":"2024-09-24T20:52:45.154Z","updated_at":"2025-10-26T02:31:15.613Z","avatar_url":"https://github.com/logicblocks.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# configurati\n\n[![Clojars Project](https://img.shields.io/clojars/v/io.logicblocks/configurati.svg)](https://clojars.org/io.logicblocks/configurati)\n[![Clojars Downloads](https://img.shields.io/clojars/dt/io.logicblocks/configurati.svg)](https://clojars.org/io.logicblocks/configurati)\n[![GitHub Contributors](https://img.shields.io/github/contributors-anon/logicblocks/configurati.svg)](https://github.com/logicblocks/configurati/graphs/contributors)\n\nA Clojure library for managing application configuration.\n\n## Installation\n\nAdd the following dependency to your `project.clj` file:\n\n    [io.logicblocks/configurati \"0.5.6\"]\n\n## Standard Usage\n\nConfigurati allows sets of configuration to be defined:\n\n```clojure\n(require '[configurati.core :as conf])\n\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-source (conf/env-source :prefix :my-service))\n    (conf/with-parameter :database-host)\n    (conf/with-parameter :database-port :type :integer)\n    (conf/with-parameter :database-schema :default \"default-schema\")))\n```\n\nThis defines a configuration that will look up parameter values from\nenvironment variables, converting `database-port` to an integer and leaving\nall other parameters as strings, defaulting `database-schema` to\n`\"default-schema\"`.\n\nAssuming an environment of:\n\n```bash\nMY_SERVICE_DATABASE_HOST=\"db.example.com\"\nMY_SERVICE_DATABASE_PORT=\"5000\"\n```\n\nthis configuration resolves to a map as follows:\n\n```clojure\n(conf/resolve database-configuration)\n; =\u003e\n; {:database-host \"db.example.com\",\n;  :database-port 5000\n;  :database-schema \"default-schema\"}\n```\n\n### Parameters\n\nEach parameter has a mandatory name, corresponding to the key used to look up\nthat parameter in the provided configuration sources. Names are always\nkeywords.\n\nParameters also have options:\n\n* `:type`: specifies the type of the resulting value. Currently, only `:any`,\n  `:string` and `:integer` are supported although the conversions are\n  extensible as detailed in the advanced usage section below. Defaults to\n  `:any`, performing no conversion of the looked up values.\n* `:nilable`: whether the parameter can be `nil`. Either `true` or\n  `false`. Defaults to `false`.\n* `:validator`: specifies a validator function or keyword referencing a spec\n  to validate the parameter against. Validation occurs post-conversion.\n* `:default`: the default value to use in the case that no configuration\n  source contains this parameter. The default value is converted before being\n  returned. Defaults to `nil`.\n\nThe `with-parameter` function accepts all of these options:\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-parameter :database-host)\n    (conf/with-parameter :database-port :type :integer)\n    (conf/with-parameter :database-scheme :default \"default-schema\")\n    (conf/with-parameter :database-timeout\n      :type :integer :nilable true :validator #(\u003c= % 30000))\n    ...))\n```\n\n### Sources\n\nEach parameter is looked up in a configuration source. A configuration source\nis anything that implements `clojure.lang.ILookup`.\n\nThere are a number of configuration sources included with Configurati as\ndetailed in the subsequent sections.\n\n#### map-source\n\n`map-source` uses an in memory map to look up parameter values:\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-source (conf/map-source\n                        {:database-username \"some-username\"\n                         :database-password \"some-password\"}))\n    ...))\n```  \n\n#### env-source\n\n`env-source` looks up parameter values from environment variables.\n\n`env-source` takes an optional prefix prepended to the parameter name before\nlook up.\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-source (conf/env-source :prefix :my-service))\n    ...))\n```\n\n#### environ-source\n\n`environ-source` uses [environ](https://github.com/weavejester/environ) to look\nup\nparameter values such that configuration can come from environment variables,\nsystem properties or via the build system (lein or boot).\n\n`environ-source` takes an optional prefix prepended to the parameter name before\nlook up.\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-source (conf/env-source :prefix :my-service))\n    ...))\n```\n\n#### yaml-file-source\n\n`yaml-file-source` loads configuration from a YAML file at the provided path.\nUnder the covers, `yaml-file-source` uses `slurp` so everything it supports,\ne.g., URIs, are also supported.\n\n`yaml-file-source` takes an optional prefix prepended to the parameter name\nbefore look up.\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-source (conf/yaml-file-source \"path/to/config.yaml\"\n                        :prefix :my-service))\n    ...))\n```\n\nThe file at `path/to/config.yaml` would look something like:\n\n```yaml\nmy_service_database_username: \"some-username\"\nmy_service_database_password: \"some-password\"\n```\n\n#### multi-source\n\nSometimes it is useful to look up configuration from a number of different\nsources that form a configuration hierarchy.\n\n`multi-source` takes a number of sources and looks up parameter values in the\norder the sources are specified at construction.\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/multi-source\n        (conf/env-source)\n        (conf/yaml-file-source \"path/to/config.yaml\")))\n    ...))\n```\n\nNote, if multiple sources are provided to `define-configuration`, a\n`multi-source` is automatically created in the background passing the sources\nin the same order as they are provided.\n\n### Source Middleware\n\nSources support middleware allowing parameter keys to be transformed before\nthey are passed to the source and parameter values to be transformed before\nthey are returned.\n\nSince middleware apply to sources, they take effect before parameter value\nconversion takes place.\n\nConfigurati includes a number of parameter value transforming middlewares\nas detailed in the subsequent sections.\n\n#### json-parsing-middleware\n\n`json-parsing-middleware` parses parameter values as JSON. It allows\nconfiguration of:\n\n* which parameters to target;\n* how to perform the parsing.\n\nBy default, `json-parsing-middleware` parses all parameter values retrieved\nfrom the source, converting keys to keywords but not changing their casing:\n\n```clojure\n(require '[configurati.middleware :as conf-mdlw])\n\n(def issuer-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source\n        {:issuer-1 \"{\\\"url\\\": \\\"https://issuer-1.example.com\\\"}\"\n         :issuer-2 \"{\\\"url\\\": \\\"https://issuer-2.example.com\\\"}\"})\n      (conf/with-middleware (conf-mdlw/json-parsing-middleware)))\n    (conf/with-parameter :issuer-1)\n    (conf/with-parameter :issuer-2)))\n```\n\nwhich resolves to:\n\n```clojure\n(conf/resolve issuer-configuration)\n; =\u003e\n; {:issuer-1 {:url \"https://issuer-1.example.com\"}\n;  :issuer-2 {:url \"https://issuer-2.example.com\"}}\n```\n\nTo convert only certain parameters, pass their parameter keys in the `:only`\noption:\n\n```clojure\n(def issuer-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source\n        {:issuer  \"{\\\"url\\\": \\\"https://issuer-1.example.com\\\"}\"\n         :timeout 10000})\n      (conf/with-middleware\n        (conf-mdlw/json-parsing-middleware\n          :only [:issuer])))\n    (conf/with-parameter :issuer)\n    (conf/with-parameter :timeout)))\n```\n\nwhich resolves to:\n\n```clojure\n(resolve issuer-configuration)\n; =\u003e\n; {:issuer {:url \"https://issuer-1.example.com\"}\n;  :timeout 10000}\n```\n\nTo configure parsing, there are two options available:\n\n* `:key-fn` replaces the function used to convert keys in the resulting map;\n* `:parse-fn` replaces the entire JSON parsing function.\n\nFor example, to kebab case keys:\n\n```clojure\n(require '[camel-snake-kebab.core :as csk])\n\n(def issuer-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source\n        {:authentication \"{\\\"issuerUrl\\\": \\\"https://issuer-1.example.com\\\"}\"})\n      (conf/with-middleware\n        (conf-mdlw/json-parsing-middleware\n          :key-fn csk/kebab-case-keyword)))\n    (conf/with-parameter :authentication)))\n```\n\nwhich resolves to:\n\n```clojure\n(conf/resolve issuer-configuration)\n; =\u003e\n; {:authentication {:issuer-url \"https://issuer-1.example.com\"}}\n```\n\n#### separator-parsing-middleware\n\n`separator-parsing-middleware` splits parameter values on a separator. It allows\nconfiguration of:\n\n* which parameters to target;\n* how to perform the parsing.\n\nBy default, `separator-parsing-middleware` parses all parameter values retrieved\nfrom the source, splitting on comma:\n\n```clojure\n(def supplier-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source\n        {:countries  \"USA,GBR,DEU\"\n         :currencies \"USD,GBP,EUR\"})\n      (conf/with-middleware (conf-mdlw/separator-parsing-middleware)))\n    (conf/with-parameter :countries)\n    (conf/with-parameter :currencies)))\n```\n\nwhich resolves to:\n\n```clojure\n(conf/resolve issuer-configuration)\n; =\u003e\n; {:countries [\"USA\" \"GBR\" \"DEU\"]\n;  :currencies [\"USD\" \"GBP\" \"EUR\"]}\n```\n\nTo convert only certain parameters, pass their parameter keys in the `:only`\noption:\n\n```clojure\n(def supplier-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source\n        {:countries \"USA,GBR,DEU\"\n         :name      \"Supplier, Ltd.\"})\n      (conf/with-middleware (conf-mdlw/separator-parsing-middleware\n                              :only [:countries])))\n    (conf/with-parameter :countries)\n    (conf/with-parameter :currencies)))\n```\n\nwhich resolves to:\n\n```clojure\n(conf/resolve issuer-configuration)\n; =\u003e\n; {:countries [\"USA\" \"GBR\" \"DEU\"]\n;  :name \"Supplier, Ltd.\"}\n```\n\nTo configure parsing, there are three options available:\n\n* `:separator` defines the character on which to split, defaulting to `\",\"`;\n* `:trim` indicates whether to trim the split values, defaulting to\n  `true`;\n* `:parse-fn` which replaces the entire parsing function, ignoring the above\n  two options.\n\nFor example, to split on pipe characters:\n\n```clojure\n(require '[camel-snake-kebab.core :as csk])\n\n(def supplier-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source\n        {:countries \"USA|GBR|DEU\"})\n      (conf/with-middleware\n        (conf-mdlw/separator-parsing-middleware\n          :separator \"|\")))\n    (conf/with-parameter :countries)))\n```\n\nwhich resolves to:\n\n```clojure\n(conf/resolve supplier-configuration)\n; =\u003e\n; {:countries [\"USA\" \"GBR\" \"DEU\"]}\n```\n\n### Key Functions\n\nYou may wish to refer to configuration parameters differently in code compared\nto how they are specified in the configuration sources. Key functions enable\nthis.\n\nWhen a configuration is defined, one or more key functions can be provided\nallowing keys to be transformed during configuration resolution. A key\nfunction receives each key and returns its replacement. A number of key\nfunctions are provided in `configurati.key-fns`.\n\n```clojure\n(require '[configurati.key-fns :as conf-kf])\n\n(def api-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source {:api-username \"some-username\"\n                        :api-password \"some-password\"\n                        :api-port     \"5000\"}))\n    (conf/with-parameter :api-username)\n    (conf/with-parameter :api-password)\n    (conf/with-parameter :api-port :type :integer)\n    (conf/with-key-fn (conf-kf/remove-prefix :api))\n    (conf/with-key-fn (conf-kf/add-prefix :service))))\n\n(conf/resolve api-configuration)\n; =\u003e\n; {:service-username \"some-username\"\n;  :service-password \"some-password\"\n;  :service-port     5000}\n```\n\n### Transformations\n\nIn some cases, you may need to change the shape of a configuration map or add,\nmodify or remove elements from it. Transformations enable this.\n\nWhen a configuration is defined, one or more transformations can be provided\nallowing the configuration map to be transformed during configuration\nresolution. A transformation function receives the resolved configuration map\nand returns it after applying the transformation.\n\n```clojure\n(def api-configuration\n  (conf/define-configuration\n    (conf/with-source\n      (conf/map-source {:username \"some-username\"\n                        :password \"some-password\"\n                        :port     \"5000\"}))\n    (conf/with-parameter :username)\n    (conf/with-parameter :password)\n    (conf/with-parameter :port :type :integer)\n    (conf/with-transformation (fn [m] {:api m}))))\n\n(conf/resolve api-configuration)\n; =\u003e\n; {:api\n;  {:username \"some-username\"\n;   :password \"some-password\"\n;   :port     5000}}\n```\n\n### Specifications\n\nA set of parameters makes up a configuration specification. Configuration\nspecifications can be created separately from defining configuration:\n\n```clojure\n(def database-configuration-specification\n  (conf/define-configuration-specification\n    (conf/with-parameter :database-host)\n    (conf/with-parameter :database-port :type :integer)\n    (conf/with-parameter :database-scheme :default \"default-schema\")))\n\n...\n\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-specification\n      database-configuration-specification)\n    ...))\n```\n\nThis is useful if different configuration sources need to be used at different\ntimes for the same configuration specification.\n\n`define-configuration` supports multiple specifications whose parameters are\nmerged together to form one specification. Additional parameters can also be\nspecified:\n\n```clojure\n(def database-configuration-specification\n  (conf/define-configuration-specification\n    (conf/with-parameter :database-host)\n    (conf/with-parameter :database-port :type :integer)\n    (conf/with-parameter :database-scheme :default \"default-schema\")))\n\n(def service-configuration-specification\n  (conf/define-configuration-specification\n    (conf/with-parameter :service-host)\n    (conf/with-parameter :service-port :type :integer)\n    (conf/with-parameter :service-token)))\n\n...\n\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-specification\n      database-configuration-specification)\n    (conf/with-specification\n      service-configuration-specification)\n    (conf/with-parameter :other-parameter)\n    ...))\n```\n\nis the same as:\n\n```clojure\n(def database-configuration\n  (conf/define-configuration\n    (conf/with-parameter :database-host)\n    (conf/with-parameter :database-port :type :integer)\n    (conf/with-parameter :database-scheme :default \"default-schema\")\n    (conf/with-parameter :service-host)\n    (conf/with-parameter :service-port :type :integer)\n    (conf/with-parameter :service-token)\n    (conf/with-parameter :other-parameter)\n    ...))\n```\n\nA configuration specification optionally takes one or more key functions\nsimilar to those described above:\n\n```clojure\n(require '[configurati.key-fns :as conf-kf])\n\n(def database-configuration-specification\n  (conf/define-configuration-specification\n    (conf/with-key-fn (conf-kf/remove-prefix :api))\n    ...))\n```\n\nWhen configuration specifications are merged as part of a definition,\ntheir key functions are composed together in the reverse order the\nspecifications are provided with any key functions on the definition itself\napplying last:\n\n```clojure\n(def specification-1\n  (conf/define-configuration-specification\n    (conf/with-key-fn (fn [key] (str \"s1-\" (name key))))))\n\n(def specification-2\n  (conf/define-configuration-specification\n    (conf/with-key-fn (fn [key] (str \"s2-\" (name key))))))\n\n(def configuration\n  (conf/define-configuration\n    (conf/with-specification specification-1)\n    (conf/with-specification specification-2)\n    (conf/with-parameter :param)\n    (conf/with-key-fn (fn [key] (str \"def-\" (name key))))\n    (conf/with-key-fn (fn [key] (keyword key)))\n    (conf/with-source (conf/map-source {:param \"val\"}))))\n\n(conf/resolve configuration)\n; =\u003e\n; {:def-s2-s1-param \"val\"}\n```\n\n## Advanced Usage\n\n### Custom converters\n\nTo add a parameter type, implement the `convert-to` multimethod in\n`configurati.conversions`:\n\n```clojure\n(require '[configurati.conversions :as conf-conv])\n\n(defmethod conf-conv/convert-to :boolean [_ value]\n  (if (#{\"true\" true} value) true false))\n\n(def configuration\n  (conf/define-configuration\n    (conf/with-source (conf/map-source {:encrypted? \"true\"}))\n    (conf/with-parameter :encrypted? :type :boolean)))\n\n(conf/resolve configuration)\n; =\u003e\n; {:encrypted? true}\n```\n\n### Custom middleware\n\nTo create a custom middleware, create a function that returns a function as\nfollows:\n\n```clojure\n(defn custom-middleware []\n  (fn [source parameter-name]\n    ...))\n```\n\nWithin the body of the returned function, call `source` with `parameter-name`\nor a derivative, transform and return the response.\n\n## License\n\nCopyright \u0026copy; 2024 LogicBlocks Maintainers\n\nDistributed under the terms of the\n[MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogicblocks%2Fconfigurati","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flogicblocks%2Fconfigurati","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogicblocks%2Fconfigurati/lists"}