Rafael Wenzel bio photo

Rafael Wenzel

Lead DevOps Engineer

Twitter Github Stackoverflow

In this article series that I call Adventures in Erlang I am trying to teach the readers how to properly start an Erlang endeavor on their own.

Please note that this article prerequisites Erlang/OTP 18 as well as Rebar as a package manager, in order to follow on your own system. I further assume that you are running on a Linux operating system. If you are running on anything else, you need to adapt the commands respectively.

I have created a free github project for this article series, that you can find here.

If you want to learn more about the Erlang syntax, and all the stuff we use in these articles, please refer to the rather awesome free online book Learn you some Erlang.

Note: This article is highly inspired by this awesome n2o tutorial.

Adding web dependency

As a frontend of our application, I want to setup some web pages. For this to work, I use the n20 framework which I can conveniently add as a rebar dependency.

So we open up our rebar.config file and add the dependency to it.

%%-*- mode: erlang -*-

{edoc_opts, [
    {application, ["adventures"]}
    ]}.

{deps, [
  {n2o, ".*", {git, "git://github.com/5HT/n2o.git"}}
]}.

Adding dependencies to our project is really as easy as adding the name and the repository to our rebar configuration, and letting nifty rebar handle all the rest.

Now, with a simple command we can get rebar to fetch and compile our dependencies.

$ rebar get-deps compile

Setting up the framework

Next we want to make sure, that the n2o framework properly starts up with our main application. So we open up our adventures_app.erl file, and change up the start/2 function in the following manner.

start(_StartType, _StartArgs) ->
    application:ensure_all_started(n2o),
    application:set_env(n2o, route, routes),
    adventures_sup:start_link().

The commands should be pretty self-explanatory here. First we make sure that n2o has everything it needs to be used, and then we set up the routes information for n2o to use.

An own supervisor

For our web server handling we want to make an own supervisor that handles all the shenanigans. That way we can just add to our main application child list and not screw with the basic files we have already set up in the last article.

So we create a new supervisor called web_sup.erl with the following content.

-module(web_sup).

-behaviour(supervisor).

%% API
-export([start_link/0]).

%% Supervisor callbacks
-export([init/1]).

%% Helper macro for declaring children of supervisor
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).

%% ===================================================================
%% API functions
%% ===================================================================

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

%% ===================================================================
%% Supervisor callbacks
%% ===================================================================

init([]) ->
    {ok, _} = cowboy:start_http(http, 3, [{port, 9002}], [{env, [{dispatch, rules()}]}]),
    {ok, { {one_for_one, 5, 10}, [] }}.

rules() ->
  cowboy_router:compile([
    {'_', [                  %% handle all domains
       {'_', n2o_cowboy, []}  %% handle all urls
     ]}
  ]).

With this new supervisor we revisit our main applications supervisor adventures_sup.erl and add it to the childrens list in the init function.

init([]) ->
    {ok, { {one_for_one, 5, 10}
      , [?CHILD(adventures_server, worker), ?CHILD(web_sup, supervisor)]
    }}.

As we see, I just added another ?CHILD call of our web_sup with a supervisor as a type. That way we created our first supervision tree, without even knowing it.

Changing our server

As a little side-note we need to change our server as we set it up last time. Because we just added a simple loop for a running server, which is actually blocking the application flow, we need to get rid of that first, otherwise our previously created supervisor is never being called. So we open up our adventures_server.erl and change the init/1 function to the following.

init(Args) ->
    %loop(),
    {ok, Args}.

We just comment out the loop/0 function, as it is not needed right now anyway.

Web content

Now that everything is properly introduced in our application, we go on to creating the web content. First we create some basic routes, and then a simple basic index page for proper demonstration.

Routes

With that we need to set up the actual routes.erl file.

-module(routes).
-include_lib("n2o/include/wf.hrl").
-export([init/2, finish/2]).

finish(State, Ctx) ->
    {ok, State, Ctx}.

init(State, Ctx) ->
    Path = wf:path(Ctx#cx.req),
    {ok, State, Ctx#cx{path=Path, module=route_prefix(Path)}}.

route_prefix(P) -> route(P).

route(_) -> index.     % always return `index` handler for any url.

This file will hold all our routes in one nifty place. We can add to it later on, and actually will, at another time when we set up some content to our web frontend.

First web page

Now to make sure that everything is working and set up properly, we create our first index page with a nice message. Let’s introduce our index.erl page.

-module(index).
-compile(export_all).
-include_lib("n2o/include/wf.hrl").

main() -> <<"Welcome to adventures in erlang!">>.   % main/0 is default function that N2O is calling

Let’s start everything up

Now we should be set to run our application and see what we created.

$ rebar compile
$ ./run.sh

And then we just browse to http://localhost:9002. We should be able to see our welcome message.

Conclusion

This article was a tough one to write. It took me a while to figure out which web framework to use, and how to properly introduce it into the main application. The state of documentation is not too helpful there, but we finally made the first step to setup our big application with a web frontend. In the next articles we will cover some more web content, and how to handle different parts of the application so everything works together like a charm.

Stay tuned.