Phoenix router got 404

I’m developing a telegram bot for twitter, here’s my router:

scope "/api", TweetBotWeb do
    pipe_through :api
    post "/#{Application.get_env(:tweet_bot, :webhook)}", TwitterController, :index
end

In dev.exs I have:

config :tweet_bot,
  webhook: "twitter"

It works fine in dev env.

In prod.secret.exs file I have:

config :tweet_bot,
  webhook: "${WEBHOOK}"

With REPLACE_OS_VARS=true I thought the release built with distillery should read the webhook from env variables, but seems not the case, the router doesn’t exists. I just got 404 when posting to the api.

If I change it to something static character, it would work fine.

Application.get_env(:tweet_bot, :webhook) would try and read :webhook at compile time (but REPLACE_OS_VARS=true works for runtime env vars?) since it’s called from inside a macro. Do you have WEBHOOK set at compile time?

In your case your configuration might be replaced with

config :tweet_bot,
  webhook: System.get_env("WEBHOOK") || raise("expected WEBHOOK env var to be set")

to make it more clear when it’s read (during compilation).

Then, during compilation, the routes macros

scope "/api", TweetBotWeb do
  pipe_through :api
  post "/#{Application.get_env(:tweet_bot, :webhook)}", TwitterController, :index
end

would look into the ets table holing application config state (Application.get_env(:tweet_bot, :webhook)), get your webhook, and interpolate it into the post route.

And the application configuration for :tweet_bot itself would be populated from your env vars (again, during compilation).

Yes, it’s set when compiling.

REPLACE_OS_VARS=true is specified to distillery, with it I can use env variables in vm.args file. What confuses me here is all the other env works just fine,

# Configure your database
config :tweet_bot, TweetBot.Repo,
  adapter: Ecto.Adapters.MySQL,
  hostname: "${DB_HOST}",
  username: "${DB_USER}",
  password: "${DB_PASSWORD}",
  database: "${DB_NAME}",
  pool_size: 15

config :tweet_bot, webhook: "${WEBHOOK}"

# Configures token for telegram bot
config :telegram_bot, token: "${TELEGRAM_TOKEN}"

# Configures extwitter oauth
config :extwitter, :oauth,
  consumer_key: "${TWITTER_CONSUMER_KEY}",
consumer_secret: "${TWITTER_CONSUMER_SECRET}"

Only this WEBHOOK one doesn’t work.

I think REPLACE_OS_VARS=true is for runtime env vars (since it’s for vm args), not compile time. The rest are probably used during runtime, so that’s why they work and WEBHOOK doesn’t.

Make sense.

I just changed the config in dev.exs to:

config :tweet_bot,
  webhook: System.get_env("webhook")

And it doesn’t work in dev anymore. Maybe phoenix doesn’t support router created from env?

Maybe phoenix doesn’t support router created from env?

I don’t think phoenix should care … And it doesn’t really know that the information comes from env since it reads :webhook from application config, which uses an ets table.

Try replacing config with

config :tweet_bot,
  webhook: System.get_env("webhook") || raise("no webhook env var set")

and see if it raises.

Before you had WEBHOOK env var, now you use webhook. Maybe that’s why it doesn’t work in dev?

It did raise error.

Could you please post what’s in the screenshot as a code snippet? If it raises, then you don’t have webhook env variable set on your machine.

Also try running webhook=twitter mix phx.routes.

I’ll try to create a test project and use the approach I outlined above. And see if it works. Maybe I’m completely wrong …

Here’s what I have running without webhook env set:

➜  tweet_bot git:(master) ✗ iex -S mix phx.server
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

** (Mix.Config.LoadError) could not load config config/dev.exs
    ** (RuntimeError) no webhook env var set
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (stdlib) erl_eval.erl:878: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:236: :erl_eval.expr/5
    (stdlib) erl_eval.erl:228: :erl_eval.expr/5
    (stdlib) erl_eval.erl:878: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:236: :erl_eval.expr/5
    (stdlib) erl_eval.erl:228: :erl_eval.expr/5
    (stdlib) erl_eval.erl:878: :erl_eval.expr_list/6

And with webhook env set:

➜  tweet_bot git:(master) ✗ webhook=twitter iex -S mix phx.server
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

[info] Running TweetBotWeb.Endpoint with Cowboy using http://0.0.0.0:4000
Interactive Elixir (1.6.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 16:57:53 - info: compiled 6 files into 2 files, copied 3 in 661 ms

Then webhook=twitter mix phx.routes:

➜  tweet_bot git:(master) ✗ webhook=twitter mix phx.routes
   page_path  GET   /               TweetBotWeb.PageController :index
   auth_path  GET   /auth_callback  TweetBotWeb.AuthController :callback
twitter_path  POST  /api/telegram   TweetBotWeb.TwitterController :index

The other webhook value:

➜  tweet_bot git:(master) ✗ webhook=hello mix phx.routes
   page_path  GET   /               TweetBotWeb.PageController :index
   auth_path  GET   /auth_callback  TweetBotWeb.AuthController :callback
twitter_path  POST  /api/telegram   TweetBotWeb.TwitterController :index 

Strange. Just tested and got this

$ WEBHOOK=twitter mix phx.routes

Compiling 10 files (.ex)
Generated env_test app
webhook_path  GET  /api/twitter  EnvTestWeb.WebhookController :index

Let me post this project to github.

https://github.com/idi-ot/env_test

And https://github.com/idi-ot/env_test/commit/80f68115ee22a14eeace872148f445303c99b873 is the relevant commit.

Call WEBHOOK=telegram mix phx.routes after WEBHOOK=twitter mix phx.routes, you will get:

$ WEBHOOK=telegram mix phx.routes
webhook_path  GET  /api/twitter  EnvTestWeb.WebhookController :index 

You’d need to recompile the project. mix phx.routes doesn’t recompile by default, it seems. But that’s a different problem.

I’ve recompiled and got

$ WEBHOOK=telega mix do compile --force, phx.routes
Compiling 10 files (.ex)
Generated env_test app
webhook_path  GET  /api/telega  EnvTestWeb.WebhookController :index

And that makes sense, since mix phx.routes seems to just return the routes from the currently compiled router.

Or maybe that’s not about mix phx.routes at all but rather nothing happens since there was no change in code (only an env var WEBHOOK was changed). So there is no way for elixir to know that the project should be recompiled.

But again, that’s a different problem if a problem at all.