gts
Getting the right Application Enviroinment in an Umbrella Project?
I’m experiencing an unexpected behaviour when accessing the application environment in an umbrella project with multiple children apps. It seems that Application.get_env/3 function doesn’t return the value stored in the config/config.exs of the child application where the function is run. Instead it returns the valued stored in the config/config.exs of the most recently added child app in the umbrella project.
To better test out the problem I’ve create a playground umbrella project adding some very basic Phoenix 1.3 apps: a shared context app (my_app) and some web apps (my_app_web_one, my_app_web_two, …)
$ mix new my_umbrella --umbrella && cd my_umbrella/apps
$ mix phx.new.ecto my_app
$ mix phx.new.web my_app_web_one
$ mix phx.new.web my_app_web_two
and made these adjustements on the scaffold files:
my_umbrella
└── apps
│
├── my_app ## The shared Ecto/Context app
│
│
│
├── my_app_web_one ## The first web app
│ ├── config
│ │ └── config.exs # 1) changed to config :my_app_web_one, ..., ecto_repos: [MyApp.Repo]
│ │ # 2) changed to config :my_app_web_one, :generators, context_app: :my_app
│ │ # 3) add entry for config :mylib, endpoint: MyAppWebOne.Endpoint
│ │
│ ├── test
│ │ ├── my_app_web_one
│ │ │ └── my_test.exs # Add test for assert Application.get_env(:mylib, :endpoint) == MyAppWebOne.Endpoint
│ │ ├── support
│ │ │ ├── channel_case.exs # Changed occurrency of {MyAppWebOne.Repo} to {MyApp.Repo}
│ │ │ └── conn_case.exs # Changed occurrency of {MyAppWebOne.Repo} to {MyApp.Repo}
│ │ └── test_helper.exs # Changed occurrency of {MyAppWebOne.Repo, :manual} to {MyApp.Repo, :manual}
│ │
│ └── mix.exs # 1) add {:my_app, in_umbrella: true} to deps
│ # 2) add {:mylib, "~> 1.0"} to deps
│
│
│
└── my_app_web_two ## The second web app
├── config
│ ├── config.exs # 1) changed to config :my_app_web_two, ..., ecto_repos: [MyApp.Repo]
│ │ # 2) changed to config :my_app_web_two, :generators, context_app: :my_app
│ │ # 3) add entry for config :mylib, endpoint: MyAppWebTwo.Endpoint
│ └── dev.exs # Changed the http port to 4040
│
├── test
│ ├── my_app_web_one
│ │ └── my_test.exs # Add test for assert Application.get_env(:mylib, :endpoint) == MyAppWebTwo.Endpoint
│ ├── support
│ │ ├── channel_case.exs # Changed occurrency of {MyAppWebTwo.Repo} to {MyApp.Repo}
│ │ └── conn_case.exs # Changed occurrency of {MyAppWebTwo.Repo} to {MyApp.Repo}
│ └── test_helper.exs # Changed occurrency of {MyAppWebTwo.Repo, :manual} to {MyApp.Repo, :manual}
│
└── mix.exs # 1) add {:my_app, in_umbrella: true} to deps
# 2) add {:mylib, "~> 1.0"} to deps
The adjustements mainly regard setting the right references for the context app in the web applications, and configuring a dependency called mylib.
the mylib entry for apps/my_app_web_one/config/ config.exs is
config :mylib,
endpoint: MyAppWebOne
while the mylib entry for apps/my_app_web_two/config/ config.exs is
config :mylib,
endpoint: MyAppWebTwo
And these are the tests for checking the value returned by Application.get_env in each web app :
for apps/my_app_web_one:
# (the namespace is of course different for each app)
defmodule MyAppOne.MyTest do
use ExUnit.Case
test "test 'mylib' endpoint for MyAppOne" do
assert Application.get_env(:mylib, :endpoint) == MyAppOne.Endpoint
end
end
for apps/my_app_web_two:
defmodule MyAppTwo.MyTest do
use ExUnit.Case
test "test 'mylib' endpoint for MyAppTwo" do
assert Application.get_env(:mylib, :endpoint) == MyAppTwo.Endpoint
end
end
Running the tests for each web app give:
- test for
apps/my_app_web_onefails becauseApplication.get_env(:mylib, :endpoint)returnsMyAppTwo.Endpointinstead ofMyAppOne.Endpoint - test for
apps/my_app_web_twosucceded.
Adding a third web app to the project called my_app_web_three, things go this way instead:
- test for
apps/my_app_web_onefails becauseApplication.get_env(:mylib, :endpoint)returnsMyAppThree.Endpointinstead ofMyAppOne.Endpoint - test for
apps/my_app_web_twofails becauseApplication.get_env(:mylib, :endpoint)returnsMyAppThree.Endpointinstead ofMyAppTwo.Endpoint - test for
apps/my_app_web_threesucceded
I’ve also tried to leave just one web app at time in the umbrella project, and test succeded every time for all the three apps.
I wasn’t able to find in the documentation for Elixir umbrella projects, for Application.get_env/3 or for the Phx generators anything that let me understand where can be the problem, neither googling on this forum or on stackoverflow.
So maybe is just me that don’t understand well how things have to work in these kinds of projects. Can you point me towards something I’ve missed?
Marked As Solved
ryh
The relevant information is here (look in the examples section where they run config :lager ... twice). You are overwriting the :endpoint key for :mylib in each successive config.
Look in config/config.exs in the root of your umbrella project.
There you will find:
import_config "../apps/*/config/config.exs"
What’s happening:
config :mylib, endpoint: MyAppWebOne.Endpoint
config :mylib, endpoint: MyAppWebTwo.Endpoint # overwritten! Application.get_env(:mylib, :endpoint) == MyAppWebTwo
config :mylib, endpoint: MyAppWebThree.Endpoint # overwritten! Application.get_env(:mylib, :endpoint) == MyAppWebThree
You must namespace each config or you will overwrite :endpoint. Usually the convention would be to do
config :my_app_one, endpoint: MyAppOne.Endpoint
config :my_app_two, endpoint: MyAppTwo.Endpoint
config :my_app_three, endpoint: MyAppThree.Endpoint
Also Liked
axelson
gts
No sorry, I haven’t any significant link. But I can share the experience I acquired trying to solve the problem answering to any more specific questions
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









