Enviable

Hex.pm Hex Docs Apache 2.0 Coverage

Enviable is a small collection of functions to improve Elixir project configuration via environment variables as proposed under the 12-factor application model. It works well with configuration environment loaders like Dotenvy, Nvir, or Envious and provides robust value conversion like jetenv.

Enviable 3.0 introduces a new interface, Enviable.Codex, separating value retrieval from conversion. The main new features in Codex:

All Enviable.Codex value retrieval functions all return a struct implementing String.Chars; it can be used in string interpolation just like values from System.get_env/1,2. Outside of string interpolation, as_string/1 is recommended.

There are breaking changes described in the CHANGELOG.

Elixir 1.17 or later is required.

Usage

Enviable or Enviable.Codex will typically be imported in config/runtime.exs after Config, but may be used anywhere that environment variables are read.

Enviable.Codex with Nvir

The Nvir dotenv* functions always return an environment map, but it automatically pushes all values back into the process environment table.

import Nvir
import Enviable.Codex

client = fetch_env!("CLIENT")
dotenv!([".env", ".env.#{client}"])

config :my_app,
  key: fetch_env_once!("SECRET_KEY") |> as_string(),
  port: fetch_env!("PORT") |> as_integer(),
  ssl: get_env("SSL_ENABLED") |> as_boolean()

Enviable with Nvir

import Nvir
import Enviable

client = fetch_env!("CLIENT")
dotenv!([".env", ".env.#{client}"])

config :my_app,
  key: fetch_env!("SECRET_KEY"),
  port: fetch_env_as_integer!("PORT"),
  ssl: get_env_as_boolean("SSL_ENABLED")

Enviable.Codex with Dotenvy

The use of a side_effect calling System.put_env/1 is discouraged with Enviable.Codex.

import Config
import Enviable.Codex

client = fetch_env!("CLIENT")
values = Dotenvy.source!([".env", ".env.#{client}", get_env()])

config :my_app,
  key: fetch_env_map!(values, "SECRET_KEY") |> as_string(),
  port: fetch_env_map!(values, "PORT") |> as_integer(),
  ssl: get_env_map(values, "SSL_ENABLED") |> as_boolean()

Enviable with Dotenvy

The use of a side_effect that calls System.put_env/1 is required with Enviable.

# config/runtime.exs
import Config
import Enviable

client = fetch_env!("CLIENT")
Dotenvy.source([".env", ".env.#{client}", get_env()], side_effect: &put_env/1)

config :my_app,
  key: fetch_env!("SECRET_KEY"),
  port: fetch_env_as_integer!("PORT"),
  ssl: get_env_as_boolean("SSL_ENABLED")

Enviable.Codex with Envious

Envious parses a single file at a time and doesn't write to the environment variable table.

import Config
import Enviable.Codex

client = fetch_env!("CLIENT")
env_files = [".env", ".env.#{client}"]

loaded_env =
  Enum.reduce(env_files, %{}, fn file, acc ->
    case File.read(file) do
      {:ok, contents} -> Map.merge(acc, Envious.parse!(contents))
      _ -> acc
    end
  end)

values = Map.merge(loaded_env, get_env())

config :my_app,
  key: fetch_env_map!(values, "SECRET_KEY") |> as_string(),
  port: fetch_env_map!(values, "PORT") |> as_integer(),
  ssl: get_env_map(values, "SSL_ENABLED") |> as_boolean()

Enviable with Envious

Envious parses a single file at a time and doesn't write to the environment variable table. To work with Enviable, we put the values into the environment with put_env_new/2.

import Config
import Enviable

client = fetch_env!("CLIENT")

env_files = [".env", ".env.#{client}"]

loaded_env =
  Enum.reduce(env_files, %{}, fn file, acc ->
    case File.read(file) do
      {:ok, contents} -> Map.merge(acc, Envious.parse!(contents))
      _ -> acc
    end
  end)

for {key, value} <- loaded_env, do: put_env_new(key, value)

config :my_app,
  key: fetch_env!("SECRET_KEY"),
  port: fetch_env_as_integer!("PORT"),
  ssl: get_env_as_boolean("SSL_ENABLED")

Installation

Enviable can be installed by adding enviable to your list of dependencies in mix.exs:

def deps do
  [
    {:enviable, "~> 3.0"},
    # Optional, required for decimal conversion
    {:decimal, "~> 2.3"},
    # Optional, required only if :json or JSON aren't available or another
    # JSON engine is not configured
    {:jason, "~> 1.4"},
    # Optional, if using the Enviable credo checks
    {:credo, "~> 1.7", only: [:dev, :test], runtime: false}
  ]
end

Documentation is found on HexDocs.

Semantic Versioning

Enviable follows Semantic Versioning 2.0.