Detecting Mix.env in library

Hi folks,

What’s the current best practice when it comes to detecting env, something like Mix.env() within a library?

I’m currently building a library, and I’m trying to figure a good way for config to return different values based on environment it’s running on.
Based on library guidelines, config.exs is not recommended (and seems to be ignored when lib is used anyways), so I add a module like Library.Config. for returning config related values.

I tried something like this

defmodule Library.Config do
  def url() do
    if Mix.env() == :dev do
      @dev_url
    else
      @event_url
    end
  end
end

which seem to work for local dev, but now I’m getting

Function Mix.env/0 does not exist.

from Dialyzer.

Would appreciate any recommendations or just pointing me to a direction.
Searching on this topic gets different results from the pass and seems like guidelines have been changing.
Also most questions seem to be more on usage within an application like Phoenix, and I don’t seem to be able to find much for library authors, so it’s not clear what’s the best way to proceed.

I’m fine with using something like Application.get_env, but unclear how the config values can be derived over to applications using the lib.
e.g.

# my lib config.exs
config :mylib, base_url: "https://someapp.com"

if Mix.env() == :dev do
  config :mylib, base_url: "http://127.0.0.1:8288"
end

## application using mylib
iex -S mix> Application.get_env(:mylib, :base_url)
# => nil

Thanks

If you need base_url to be set when you’re developing your library locally, then you already figured out how to do it:

# my lib config.exs
config :mylib, base_url: "https://someapp.com"

But if you need applications using the library to pass configuration to it, they will have to do so in their own config files. You should also consider whether it’s a good idea for your library to avoid using the application configuration altogether.

1 Like

You should also consider whether it’s a good idea for your library to avoid using the application configuration altogether

Yes, that’s why I moved towards creating a Config module to handle things.
Then I now hit the Dialyzer error because I’m using Mix.env within the code to change return values based on the environment.

Hence, my initial question.

What’s the current best practice when it comes to detecting env, something like Mix.env() within a library?

Are there better ways to determine the lib env besides Mix.env()?

Libraries are always compiled with Mix.env == :prod, so I think toggling based on that isn’t going to work. Libraries should not change themselves implicitly, the user of the library should always pass in configuration. What changes are you trying to do?

3 Likes

I wouldn’t do it for at least 2 reasons:

  • libraries can also run outside of Mix, if your application is running in a release for example then Mix isn’t even available so the notion of an environment at runtime doesn’t even make sense
  • environment names are completely defined by the application using your lib. Most people use standard names like dev, test, prod etc. but they could use whatever. For all you know, they could use development instead of dev

If your library needs to consume a configuration, then have your library’s user pass that in. Only that should matter, not the name of the environment

1 Like

What I’m currently working on is, if dev, use a local base url 127.0.0.1:8288, if not dev use a remote base url https://inn.gs.

I’m basically building an Elixir SDK for Inngest following some of the existing JS SDK patterns.
Most of the values can be read from env vars as well, but I was hoping to provide some kind of good defaults.

Yeah, good point.
Maybe I really just have to expose the option as a Keyword, and provide some default values like dev: false or something of the kind.

The convention in this case would be that your install instructions would tell users to do this in their runtime.exs

config :your_lib, endpoint: "127.0.0.1:8288"

if config_env() == :prod do
  config :your_lib, endpoint: "[127.0.0.1:8288](https://inn.gs)"
end

Using the mix.env as a global variable to change logic in. your library both won’t work for the reasons I mentioned earlier, and is not a best practice.

1 Like

As a corollary, you can use NimbleOptions to help you with having solid configuration values.

1 Like

this looks nice! will definitely take a closer look!

okay, got it. I was rather reluctant to do that due to more setup work for the user, but I guess that’s pretty much how it has to be if going with using configs