How to use if or other condition for "dev" or "prod"

Hi!
I want to do something like this.
if MIX_ENV == “dev” do
do if in local dev environment
else
do if in production environment
end

Is this possible?

1 Like

In my experience, this a bad idea and there are many times that this can be avoided (but not always).

First of all, Mix is a tool which is not available in a release, so it can be dangerous if you use in dev.

I don’t know what you are trying to do, but for example, if you don’t want to send emails in dev, it should have its own configuration. Maybe this configuration relays on an environment variable in production, but it should not have a generic name, it would be better something like MAIL_CONFIG.

Maybe the following links help:

https://hexdocs.pm/mix/Mix.Config.html

1 Like

i tried this in config.exs and it works

if "#{Mix.env}" === "dev" do
  IO.puts "I am in dev environment"
end

In the config, this construct is safe to use, but I’m wondering why you don’t use environment specific config files?

Also instead of stringifying you should compare atoms directly: Mix.env() == :dev

1 Like

You can add config :myapp, :environment, :dev to dev.exs and config :myapp, :environment, :prod to prod.exs and then in your function:

if (Application.get_env(:myapp, :environment) == :prod) do
  # prod branch
else
  # dev/test branch
end
2 Likes

If you want to do it at runtime

if unquote(Mix.env == :dev) do # would compile to true or false
  # ...
else
  # ...
end

However I prefer to compile different functions / bodies depending on the mix env / runtime version, rather than using if branching inside of them.

defmodule SomeModule do
  if Mix.env == :test do
    def func(), do: :ok  
  else
    def func(), do: # actually do soemthing
  end
end

but I use it very rarely (mostly in routers / plug pipelines), something like a protocol is usually a cleaner approach.

2 Likes

Why not

if Mix.env == :dev do
  IO.puts "I am in dev environment"
end

?

Since config.exs is only evaluated during compilation, it’s safe to call Mix.env without unquoting there.

From what I understand, he wants his code to behave differently when deployed.

As @alexcastano mentioned, Mix is not available in a release. It will work while you run in dev but will not work in the production environment.

That’s why you should use unquote(Mix.env == :dev) during runtime … And config.exs is not being evaluated in a release, so it doesn’t matter.

In my case, I wanted to read DB username and password from env variables.
I have experience with Nodejs that you can put a .env file in root project and every time the projects loads, the variables are loaded.
This is what I did in Phonix for that:
I have created a dev.secret.exs in config folder that is ignored in git.
after that I put my env variables in it like :
System.put_env("DATABASE_USER", "something")
and then imported this file on top of the dev.exs file with condition of dev mode:

if Mix.env == :dev do
  import_config "dev.secret.exs"
end

Secrets in Phoenix shouldn’t go in any configuration that is not the config/runtime.exs, otherwise they will be shipped inside the release, therefore they can be leaked during CI/CD pipelines or deployment process. They can also be extracted from inside the release with reverse engineering techniques.

Using config/runtime.exs with system environment variables guarantees that secrets will always be retrieved from the server running the release.

# config/runtime.exs

config :cms, Cms.Repo,
  username: System.fetch_env!("POSTGRES_USER"),
  password: System.fetch_env!("POSTGRES_PASSWORD"),
  database: System.fetch_env!("POSTGRES_DB") <> "_" <> Atom.to_string(config_env()),
  # database: System.fetch_env!("POSTGRES_DB"),
  hostname: System.fetch_env!("POSTGRES_HOSTNAME"),
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

if config_env() == :prod do
  config :cms, Cms.Repo,
    show_sensitive_data_on_connection_error: false
end

The .env file:

POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=cms4devs
POSTGRES_HOSTNAME=database

Export the secrets with:

export $(grep -v '^#' .env | xargs -0)

or if using docker-composer.yml:

env_file:
      - .env
1 Like

Is there anyway to run export $(grep -v '^#' .env | xargs -0) in phoenix runtime?
I mean when you run mix phx.server some how this export command get run

I have a config system based on config/runtime.exs and I use DotenvParser to load configuration from .env during development (but no reason you couldn’t use it in production too).

Here’s a blog post about it for more details: Simple Configuration Setup for Elixir Projects (v1.11+) – Random Notes

Disclaimer: I wrote DotenvParser.

2 Likes

You can also write your .env file using export statements, so instead of having VARIABLE="something", you would use export VARIABLE="something" and then run . .env && iex -S mix phx.server
You could also add an alias to your shell to do that, for instance, if using bash, on your .bashrc file, adding alias iexphx=". .env && iex -S mix phx.server" (refresh the shell after saving, with exec bash) and then you can just run iexphx from the command line when inside a working directory.

2 Likes

I used this trick in the past, but I forgot about it. Thanks for reviving it :slight_smile:

I am using docker for any programming language and developer tool I need to run in my computer for several years now, thus the .env file cannot use the trick you mention, but for others not using docker it makes all the sense and is indeed better, especially with your recommended alias :slight_smile:

Learned another trick :slight_smile:

I usually refresh the shell with . ~/.zshrc or . ~/.bashrc.

2 Likes

That works too!

1 Like