Configuring multiple endpoints in Phoenix Framework

By Paweł Pikuła | June 19, 2018

Configuring multiple endpoints in Phoenix Framework

In this post I want to show how to add another endpoint in Phoenix Framework, that is going to listen on a different port. I found out that this topic is not well described in the internet. In our case, this endpoint will be serving a very simple API returning status of the app. The following instructions work with Phoenix version 1.3.2

A separate endpoint gives extra security

The first question that comes to mind is “why?”. Why do we need to have a service on a separate port or even on a separate network interface? The biggest advantage and the most important one is security. By port/interface separation we can easily protect our service from unwanted traffic. For example: Our load balancer is connected to port 80 where we expose public API however, in our app and we want to expose private diagnostic API on port 8080 as well - by doing it public traffic can only hit our public API(80). We get pretty good protection of diagnostic API without authentication and authorization - of course we may still want to do that - to restrict access, even to traffic from our private network. Thanks to the separation some of it can be achieved on the firewall/routing level. Apart from that there are many examples, why it makes sense: admin interface, node stats page, health check and more.

Configuration first

Lets see how to do it on very simple phoenix app, first we need to create a new project - we can skip ecto as there will be no use for it in our example:

mix phx.new mendpoint --no-ecto

After starting the application in the dev mode, there will be a listener on port 4000 - in other words we are able to access our website via localhost:4000. Our endpoint configuration sits in: confing/dev.ex and looks like this:

We can see that the port is configured in the http section - these are params passed to Plug.Adapters.Cowboy apart from port we can configure there things like: ssl, ip address on which we listen, compression, maximum number of connections and more.

As we are here, we will start with inserting a configuration for our new endpoint and then do everything to make it work.

As presented above new endpoint will be listening on port 9999 and will be located in MendpointDiag module.

New endpoint module

As expected, by adding just a configuration nothing really happens. We need to add module that we are referencing in the config - MendpointDiag.Endpoint. We can just replicate MendpointWeb for this purpose:

I looks almost the same as MendpointWeb. There is one difference apart from the name - we plug MendpointDiag.Router instead of MendpointWeb.Router to make it work we need to create the router module as well.

Note that we used the API pipeline here as we want to return simple json response, there is no need for complicated, templated views and layouts. In the routes we can easily spot PageController, so let’s add it as well to have the last part of puzzle:

Very basic controller that just returns {"status": "ok"} response no matter what, cause Elixir services never go down ;).

Lest start the server in interactive mode(iex -S mix phx.server) and try the following:

:inet.i
Port  Module   Recv Sent Owner     Local Address Foreign Address State     Type
30545 inet_tcp 0    0    <0.332.0> *:terabase    *:*             ACCEPTING STREAM

terrabase is port 4000, so no trace of our newly added endpoint…

Who does start the endpoints?

We learned that endpoints are not started automatically, based on either the configuration entries or directory structure convention. We are in the BEAM world, so we should start looking for startup sequence in the application module and there it is! Endpoints are started as supervisors attached directly to the app supervisor. The only thing wee need to do is to add supervisor(MendpointDiag.Endpoint, []) to the children list. Additionally, we should add it to config change handler.

Having the changes above let’s restart the server:

iex -S mix phx.server

Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

[info] Running MendpointWeb.Endpoint with Cowboy using http://0.0.0.0:4000
[info] Running MendpointDiag.Endpoint with Cowboy using http://0.0.0.0:9999
Interactive Elixir (1.6.4) - press Ctrl+C to exit (type h() ENTER for help)

The Log messages are very promising, we can spot our endpoint and corresponding module in it. We go and type localhost:9999 in our browser and voalá, we can see

{"status": "ok"}

Summary

With this simple example, we have shown how easy it is to extend phoenix framework, especially knowing how standard OTP application is designed. It would be nice to have mix generator for it, but on the other hand productivity gain will be small, as one usually set up it once at the beginning of a project.

Veteran Elixir/Erlang Team Available

Are looking for Elixir or Erlang experts?
You are in the right place! We truly love working with that technology, and as a side effect, it turned out that we have mastered it.

Schedule a call with our expert
comments powered by Disqus