This is step-by-step guide on how to create your Erlang service with the powerful Enot build system. Ensure you have Enot and Erlang installed locally.

As a result you will build http service, which will listen the `8080` port and response JSON with service’s version and `MAGIC_GREETING` operation system’s environment variable or simple “hello world” on every request.
The complete source code is available on GitHub.

1.1 Create a service

Run `enot create example_service`.

It will create `example_service` directory with `enot_config.json`  and `src` for source files.

1.2 Add dependencies

`enot_config.json` is your project’s configuration. Like `pom.xml` or `build.gradle` in Java world it describes app dependensies and much more. As our example service will use cowboy to handle http requests and return JSON we need to add several dependencies to enot_config.

{
 "name": "example_service",
 "app_vsn": "0.0.1",
 "deps": [{
 "name": "cowboy",
 "url": "https://github.com/ninenines/cowboy.git",
 "tag": "2.0.0-pre.9"
 },
 {
 "name": "jsone",
 "url": "https://github.com/sile/jsone.git",
 "tag": "v0.3.3"
 }
 ]
}

1.3 Add optional system environment variable to application’s configuration

`src/example_service.app.src` is your application’s configuration. Like `application.properties` in Java world it describes application’s settings.  Let’s add new environment variable here.

{% if MAGIC_GREETING is defined %}
      {greeting, "{{ MAGIC_GREETING }}"}
{% else %}
      {greeting, "hello world!" }
{% endif %}

This is Jinja2 template. You can use it inside you app.src. This will add `greeting` application environment variable. If `MAGIC_GREETING` environment variable is available in your operation system – `application:get_env(greeting)` will return its value. If it is not specified – it will return “hello world”. You can read more about templating here.

2.1 Attach cowboy

Next step is to make our service accessible via http. Let’s set up `cowboy` library.
Create `src/es_handler_mgr`. It is Example Service’s handler manager and is responsible for configuring and starting cowboy.

-module(es_handler_mgr).
-define(LISTENER, example_listener).
-define(ROUTES(R), cowboy_router:compile([{'_', R}])).
-export([init/0]).
init() ->
 Dispatch = ?ROUTES([{'_', es_handler, #{}}]),
 {ok, _} = cowboy:start_clear(?LISTENER, 100, [{port, 8080}], #{env => #{dispatch => Dispatch}}),
 ok.

Here we start cowboy on port `8080` and ask it to handle all the requests in the `es_handler` module.

2.2 Create handler

We still don’t have `es_handler` module to handle incoming requests. Time has come to create it.

-module(es_handler).
 -export([init/2]).
 -define(JSON, #{<<"content-type">> => <<"application/json">>}).
init(Req0, State) ->
  {ok, Vsn} = application:get_key(example_service, vsn),
  Path = cowboy_req:path(Req0),
  Qs = cowboy_req:qs(Req0),
  es_log:info("New request ~p : ~p", [Path, Qs]),
  {ok, Greeting} = application:get_env(example_service, greeting),
  Req = cowboy_req:reply(200, ?JSON,
    jsone:encode(#{<<"vsn">> => list_to_binary(Vsn), <<"msg">> => list_to_binary(Greeting)}), Req0),
  {ok, Req, State}.

owboy calls `es_handler:init/2` every time it gets the connection.
Here we just return a JSON with our application’s version and greeting message from application’s environment. Application’s version is set in your project’s configuration file `enot_config.json`.

2.3 Call manager to init http

Last thing to do to make application work is to call `es_handler_mgr:init()` from our application.
Modify `example_service_app:start/2` method.

start(_StartType, _StartArgs) ->
  ok = es_handler_mgr:init(),
  case 'example_service_sup':start_link() of
    {ok, Pid} ->
      io:format("example_service started~n"),
      {ok, Pid};
    Error ->
      Error
  end.

3.1 Build a release

Run

export MAGIC_GREETING="hello enot"
enot release

You will see something like this:

INFO:enot:fetch /tmp/enot/cowlib 
WARNING:enot:Dep dep_proper not specified 
INFO:enot:fetch /tmp/enot/ranch 
INFO:enot:build deps for example_service 
INFO:enot:build deps for cowboy 
INFO:enot:build deps for cowlib 
INFO:enot:build deps for ranch
INFO:enot:build deps for jsone
INFO:enot:enot build example_service
===> Starting relx build process ... 
===> Resolving OTP Applications from directories: 
         /home/tihon/Projects/github/example_service/ebin 
         /home/tihon/Projects/github/example_service/deps 
         /usr/lib/erlang/lib 
         /home/tihon/Projects/github/example_service/_rel 
===> Resolved example_service-0.0.1 
===> Including Erts from /usr/lib/erlang 
===> release successfully created!

This means that cowboy and all its dependencies were fetched, example_service was built and release in `_rel` directory was generated.

3.2 Run and check

Run `_rel/example_service/bin/example_service console`.

If everything is fine you will see:

example_service started
Eshell V9.0  (abort with ^G) 
(example_service@127.0.0.1)1>

To access your service run in another terminal `curl 127.0.0.1:8080/`.  You should get `{“msg”:”hello enot”,”vsn”:”0.0.1″}` or `{“msg”:”hello world”,”vsn”:”0.0.1″}` if you forgot to export MAGIC_GREETING variable 😉

Here you see how fast and easy it is to build Erlang service with Enot build system. Deployment and installation will be covered in another article.

By Val

Leave a Reply