Development

Development

To get info about new technologies, perspective products and useful services

BigData

BigData

To know more about big data, data analysis techniques, tools and projects

Refactoring

Refactoring

To improve your code quality, speed up development process

Create Erlang service with Enot

Create Erlang service with Enot

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.

One Reply to “Create Erlang service with Enot”

Leave a Reply

Your email address will not be published. Required fields are marked *