emadalam

emadalam

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:

https://github.com/emadalam/mahaul

Showing Posts 1 to 10

D4no0

D4no0

Not bad!

The only issue I have with this library is the fact that you have to define the variables in separate modules. I think it would be nice to be able to define the variables directly in dev.exs, runtime.exs, and most probably it can be achieved with some metaprogramming magic.

stevensonmt

stevensonmt

I really like the idea of compile time guarantees for env variables at first glance, but I guess it’s false confidence b/c the env can always change on you after compilation leading to runtime errors. Still seems like a nice useful library. Thanks for sharing.

emadalam

emadalam OP

@D4no0 I’m not sure if I understand the issue, can you please provide some example or reference?

You don’t have to create separate modules, rather only a single module for initializing your environment variables configurations. mahaul follows the similar patterns used by other libraries from the elixir community like ecto, phoenix and many more.

If I have to draw parallels with say ecto, you initialize your MyApp.Repo once using the ecto provided Ecto.Repo module, passing configurations. And ecto, at compile time, generates the functions and needed interfaces on your MyApp.Repo which you can use later on in your app. mahaul does exactly the same, you initialize your MyApp.Env once using the Mahaul module, passing configurations. mahaul will then generate the methods needed to access your environment variables on your MyApp.Env which you can use later on from any part of your elixir app.

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()
emadalam

emadalam OP

@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
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.

D4no0

D4no0

Oh I see, this is a limitation of elixir configs. It would be nice to also have a macro or sigil to define a typed/checked environment variable directly into config, without generating the function at compile-time of course, as I hate having to look into 10 different files to find the actual config.

emadalam

emadalam OP

@D4no0 IMHO I don’t think elixir configs are limiting in any way, if anything I’ve found it to be very well thought and structured with perfect balance of standardization vs flexibility. I’ve rarely come across anything so flexible and feature complete that’s baked in as part of the core language feature, as elixir configs.

I feel it’s important to understand there’s a difference between app configuration and environment variables and they are not a replacement to each other rather environment variables compliment your app’s configurability. You can choose to configure your app with static values or environment variables, the choice is totally up to you. The fact that elixir configs lets you achieve this in a feature complete way, in itself is wonderful to me.

D4no0

D4no0

The usage of environment variables is not in question here, the thing that I try to point out is that I would like to use this library, however I would like the environment variable specification defined in the config.exs directly, not with a separate module.

In my opinion there is no reason to create your own wrappers over the config way of storing those values, however a check of the environment value type is a great thing.

To make my point cleaner, I will illustrate an dev.exs example:

import Config
# Import or use your module here
import MagicMacro

config :my_app, MyApp.Endpoint,
    http: [port: safe_env(PORT: [type: :port, default: "4000"])]
LostKobrakai

LostKobrakai

You can only do that with Config.Provider implementations (e.g. runtime.exs), but that won’t be able to provide compile time configuration. The chicken/egg situation means config first then applications, because you need to be able to configure said applications. So applications cannot be available or be used in config/config.exs.

D4no0

D4no0

Ah true, quite sad but it makes sense. Well at least for runtime.exs it would be nice to have the check.

Where Next? Top

Trending in Announcing Top

woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. https://github.com/woylie/doggo Features Unstyled Phoenix components....
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
anuaralfetahe
Hello Published a new library - ProcessHub! ProcessHub is a library designed to manage process distribution within the Elixir cluster. ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
sergio
It’s not that it’s vocabulary is too advanced. It’s something worse. I get lost trying to follow even a paragraph written by Claude. It’...
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews