Intro
One of confusions for me when work with Elixir is configuration. That not same with common configuration style I worked before.
So this topic I will deep dive to configuration in Elixir for more clearly then newbies don't be confused with Elixir's configuration.
We will talk about two main things, one is application configuration (or application environment), another is OS environment.
Application configuration
Application configuration is so confused, That separated to compiled time & runtime. And compiled time has several type like dev, prod, test & general configuration. People so confused because of these.
Original of Elixir application configuration is from Erlang OTP application environment variables (start with -env or in application *.app.src file). That is root cause make dev from other languages are confused.
In Elixir, we have compiled time & runtime configuration.
Usually, we have two way to declare configurations for an Elixir application.
The first one is add config directly to application function mix.exs file like:
def application do
[env: [media_path: "/public/media"]]
end
The second one is declare configurations in config files (recommended way). For this way we add configurations to config file: config.exs, dev.exs, prod.exs, test.exs & runtime.exs.
Compile & load flow of config files:
{config.exs -> check env & load config follow dev/prod/test env}(compile time) -> {load runtime.exs}(runtime time) -> execute application.
For functions to work with config file we have some functions style to care:
- Application.compile_env for working with compiled config this function will help Elixir compiler can detect unexpected behavior for config.
- Application.{get_env/get_all_env/fetch_env/fetch_env!}
Example config.exs file.
import Config
config :super_backend,
media: "/public/media"
import_config "#{config_env()}.exs"
dev.exs file
import Config
config :super_backend,
public_storage: "/tmp/super_backend/storage"
test.exs file
import Config
config :super_backend,
public_storage: "./public/storage"
media: "/test/data_sample/media"
dev.exs file
import Config
config :super_backend,
public_storage: "/public/storage"
runtime.exs file
import Config
config :super_backend,
public_storage: System.get_env("MEDIA_STORAGE_PATH")
media: System.get_env("MEDIA_PATH")
Explain: Elixir application will load config.exs first, then depend environment of running prod/dev/test application will compile file follow that env (prod.exs/dev.exs/test.exs) and merge (deep merge - values from dev/prod/test will override values in config.exs if these exist in both files) config. After that at runtime application will compile runtime.exs and merge it again.
For more details. We have config.exs for common/generic configurations, dev.exs for development environment, prod.exs for product environment, test.exs for test environment (remember config/dev/prod/test config file is executed in compile time then we cannot change it after build application (but can override it, not recommended) and another file is runtime.exs for all environments & support change config at runtime.
For declare config in config files we need to understand two things. The first is when the configurations are load to code (compile time for config.exs, dev.exs, prod.exs & test.exs and runtime for runtime.exs). The second is environment of configurations (dev/prod/test).
For runtime configurations, when build Elixir application with mix tool runtime.exs file will be copied to folder of release, we can change later if needed (relative path: _build/#{Mix.env()}/rel/#{app name}/#{app version}).
To test config in prod env we can run with MIX_ENV like:
MIX_ENV=prod mix phx.server
or put to env file:
export MIX_ENV=prod
export MEDIA_PATH="/public/media"
then run:
source .env.prod && mix phx.server
Another thing we need to know in here is all configurations can be delete/override in runtime (depends the way we load config to our code) then it can give a little of bit flexibility.
For config files, configurations are lived inside Erlang VM (not related with OS environment) and config of an application can be override by other application (usually, and Elixir application like Phoenix will run several Elixir application in same Erlang VM). For this we need to avoid some application name can make us confuse.
For case we need add/override config in runtime we can use Application.put_env/put_all_env functions.
For custom config style for application env, we can use Config.Provider & Config.Reader for build a custom format config or simple add a external configuration while the system boots.
OS Environment Variable
Some time we need get config from OS environment variables. For this case, we can get by function System.get_env
, for better code clean & easy to track we can push code to get config from OS env to runtime.exs and in application get config like above way.
Example for get config from OS env
.env file:
set MEDIA_PATH=/public/media
runtime.exs file:
config :super_backend,
media: System.get_env("MEDIA_PATH", "/default_public/media")
in Elixir code:
media_path = Application.get_env(:super_backend, :media)
Or if we don't want to declare in config file we can call directly like:
media_path = System.get_env("MEDIA_PATH", "/default_public/media")
Third party library
Actually, for realtime configurations problem we can use library like Vapor that support more convenient & like with common config style with file type: JSON, YAML, TOML, environment variables and .env file.
Conclusion
Now we got the way of Elixir configuration works, I think better way is got some full examples/use cases but time is limited. I will back and add more in the future.
Top comments (0)