gts

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_one fails because Application.get_env(:mylib, :endpoint) returns MyAppTwo.Endpoint instead of MyAppOne.Endpoint
  • test for apps/my_app_web_two succeded.

Adding a third web app to the project called my_app_web_three, things go this way instead:

  • test for apps/my_app_web_one fails because Application.get_env(:mylib, :endpoint) returns MyAppThree.Endpoint instead of MyAppOne.Endpoint
  • test for apps/my_app_web_two fails because Application.get_env(:mylib, :endpoint) returns MyAppThree.Endpoint instead of MyAppTwo.Endpoint
  • test for apps/my_app_web_three succeded

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

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

axelson

Scenic Core Team

@gts do you have a link to any information on writing umbrella aware libraries?

gts

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

Where Next?

Popular in Questions Top

greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: <h1>Create Post</h1> <%= ...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement