emadalam

emadalam

Mahaul - Supercharge the environment variables usage in your Elixir app

Hey all :wave:

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
  • mix environment 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 :heavy_heart_exclamation:

Most Liked

LostKobrakai

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

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

:heavy_heart_exclamation: :heavy_heart_exclamation: :heavy_heart_exclamation:

emadalam

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

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

stevensonmt

LIGHTBULB
I get it now. Thanks!

Where Next?

Popular in Libraries Top

tmbb
I’ve been working on two packages (not on hex.pm yet) to build admin interfaces for phoenix apps: bureaucrat - which contains a bunch ...
New
Crowdhailer
The latest release of Ace (0.10.0) includes serving content over HTTP/2. I have started writing a webserver to teach my self more about...
New
mbuhot
Leverage Open Api 3.0 (Swagger) to document, test, validate and explore your Plug and Phoenix APIs. Generate and serve a JSON Open API ...
New
josevalim
EDIT: since Ecto 3.0 final version is out, this post was amended to use the final versions in the instructions below. Hi everyone, We a...
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
tfwright
After working on it for a couple of months and using it in production for most of that time, today I’ve released LiveAdmin, a LiveView ba...
New
tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
martinthenth
Hello everybody :wave: Recently, some of my colleagues talked about database ids and uuids and their problems, and I remembered the pain...
New
Antrater
Hi everyone! I’m thrilled to announce a huge thing. We have been developing Elixir Moon Design System for quite a while. We are finally ...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New

Sub Categories:

We're in Beta

About us Mission Statement