emadalam
Mahaul - Supercharge the environment variables usage in your Elixir app
Hey all ![]()
I just published mahaul package for streamlining the environment variables usage in your Elixir apps. Parse and validate your environment variables easily in Elixir with the following benefits.
- Compile time access guarantees
- Parsed values with accurate elixir data types
- Validation of required values before app boot
-
mixenvironment specific defaults and fallbacks
Read more for understanding why to use this package and its benefits. The complete documentation for mahaul is available online at HexDocs.
We have been using this in production and it has already saved us from bad releases where we deployed to production and forgot to set the needed environment variables after an update. Though it seems to work nicely for a variety of use cases, I’m relatively new to Elixir (only a few months) so I’d love your inputs and reviews on this.
Thanks to this wonderful Elixir community, I’ve never come across anything like this from a community standpoint, so hats off, I’m enjoying every moment of it since the day I started learning Elixir ![]()
Most Liked
LostKobrakai
I really like the approach taken on this one, as it doesn’t try / need to overtake how configuration works. I just sits between pulling the variables out of the system env and putting them into the configuration flow as it exists.
emadalam
Can you give some brief example of how such a dynamic pattern matching can work at compile time? Form what I can think of, the following will give you runtime errors without any compile time warnings.
defmodule Env do
def get_env("VAR_1"), do: "..."
def get_env("VAR_2"), do: "..."
end
defmodule SomeModule do
def something() do
Env.get_env("VAR_1") # works
Env.get_env("VAR_2") # works
Env.get_env("VAR_3") # runtime error with no compile time warnings
end
end
vs
defmodule Env do
use Mahaul,
VAR_1: [type: :str],
VAR_2: [type: :num]
end
defmodule SomeModule do
def something() do
Env.var_1() # works
Env.var_2() # works
Env.var_3() # compile time warnings for non-existing method call
end
end
![]()
emadalam
@stevensonmt I believe you misunderstood the compile time guarantees provided by the package. mahaul does not check your environment variables at compile time, that would be rarely of much use. What it instead does is, it generates functions on you module at compile time. And the confidence and guarantees you get are from using the generated functions from your code instead of using System.get_env with strings where there is no compile time checks if you used the correct environment variables names from various places in your code. Furthermore, the generated functions returns parsed values as per your provided configuration. So you can confidently use those elixir type values in your code instead of always manipulating strings that you get traditionally.
Regarding runtime guarantees, mahaul also generates validate/0 and validate!/0 on your module and it is recommended to add those in config/runtime.exs for :dev and :prod mix environments respectively. This will ensure that in dev environment you’d get a warning if some environment variables are missing or invalid, and an exception raised in prod environment which will fail to boot your app unless all needed environment variables are set correctly.
defmodule MyApp.Env do
use Mahaul,
PORT: [type: :port, default_dev: "4000"],
DATABASE_URL: [type: :uri],
DATABASE_POOL_SIZE: [type: :int, default: "10"]
end
Now you can use the following generated methods to access your environment variables from anywhere in your elixir app code.
MyApp.Env.port()
MyApp.Env.database_url()
MyApp.Env.database_pool_size()
Also add the following in config/runtime.exs to ensure the runtime guarantees.
import Config
if config_env() == :dev do
MyApp.Env.validate()
# set configs
config :my_app, MyApp.Endpoint,
http: [port: MyApp.Env.port()]
end
if config_env() == :prod do
MyApp.Env.validate!()
# set configs
config :my_app, MyApp.Repo,
url: MyApp.Env.database_url()
pool_size: Atrium.Env.database_pool_size()
end
D4no0
Well, the point of configuration is to use them in your code later, just as libraries use them and you can do that easily by calling get_env/3 or fetch_env/2, however taking in consideration the limitation of the configuration/compilation it seems that the only way to enforce something at compile-time is to make a custom module.
It would be great if such a functionality were to be introduced somewhere in the elixir internals so it could be used in all configurations.
stevensonmt
LIGHTBULB
I get it now. Thanks!







