Cowboy and Batman.js for Erlang Web Development

Why Cowboy and Batman.js

There are a lot of Erlang web frameworks out there today. Not all are modeled after the MVC model (see Nitrogen), but I think all of them are addressing the problem the wrong way. I recently gave a presentation, slides here and the code for this example here, describing my perferred method for using Erlang for web development and why I think it is the best way to go. In this post, I’ll go into more details on how to build the Erlang backend for the TodoMVC clone I did with Batman.js. I will not spend time on Batman.js but instead only give a quick list of reasons I prefer it to other Javascript frameworks.

Batman.js advantages:

  • Automatic URL generation based on model
  • HTML data-bind templates
  • Coffeescript

Cowboy is a newer Erlang web server that provides a REST handler based on Webmachine. Both of these are perfect for developing a RESTful API, because they follow the HTTP standard exactly and when you are building an API based on HTTP, being able to properly reason about how the logic of the application maps to the protocol eases development and eases getting REST “right”.

Nginx

Any non-dynamic content should be served by Nginx since there is no logic needed and it is something Nginx is great at, so why have Erlang do it? The snippet below configures Nginx to listen on port 80 and serve files from bcmvc’s priv directory. Each request is checked to see if it is a POST or any other method with a JSON request type. If either of those are true, the request is proxied on to a server listening on port 8080, in our case the Cowboy server.

server {
  listen 80;
  server_name localhost;

  location / {
    root   <PATH TO CLONE>/bcmvc/lib/bcmvc_web/priv/;

    if ($request_method ~* POST) {
      proxy_pass        http://localhost:8080;     }

    if ($http_accept ~* application/json) {
      proxy_pass        http://localhost:8080;     }
  }
}

The API

Batman.js knows what endpoints to use and what data to send based on the name of the model we created and the encoded variables, code here. This results in the following API:

 
Method Endpoint Data Return
POST todos {todo : {body:”bane wants to meet, not worried”,isDone:false}}
PUT /todos/33e93b30-2371-4071-afc5-2d48226d5dba {todo : {body:”bane wants to meet, not worried”,isDone:false}}
GET todos [{todo : {id:"33e93b30-2371-4071-afc5-2d48226d5dba", body:"bane wants to meet, not worried", isDone:false}}]
DELETE /todos/33e93b30-2371-4071-afc5-2d48226d5dba

Cowboy Dispatch and Supervisor

Dispatch rules are matched by Cowboy to know what handler to send the request to. Here we have two rules. One that matches just the URL /todos and one that matches the URL with an additional element which will be associated with the atom todo. Both requests will be sent to the module bcmvc_todo_handler.

Dispatch = [{'_', [{[<<"todos">>], bcmvc_todo_handler, []},
                   {[<<"todos">>, todo], bcmvc_todo_handler, []}]}],

Cowboy provides a useful function child_spec for creating a child specfication to use in our supervisor. The child spec here tells Cowboy we want a TCP listener on port 8080 that handles the HTTP protocol. We additionally provide our dispatch list for it to match against and pass on requests.

ChildSpec = cowboy:child_spec(bcmvc_cowboy, 100, cowboy_tcp_transport, 
                              [{port, 8080}], cowboy_http_protocol, [{dispatch, Dispatch}]),

Cowboy Handler

Now that we have a server on port 8080 that knows to send certain requests to our todo handler, we can build the module. The first required function to export is init/3. This function let’s Cowboy know we have a REST protocol, this is how it knows what functions to call (some have defaults and some existing in our module) to handle the request.

init(_Transport, _Req, _Opts) ->
    {upgrade, protocol, cowboy_http_rest}.

Knowing that this is a REST handler Cowboy will pass the request on to allowed_methods/2 to find out if our handler is able to handle this method. Next, the content types accepted and provided by the handler are checked against the incoming request. The expected HTTP response status codes are returned if any of these fail. 405 for allowed_methods, XXX for content_types_accepted and XXX for content_types_provided.

allowed_methods(Req, State) ->
    {['HEAD', 'GET', 'PUT', 'POST', 'DELETE'], Req, State}.

content_types_accepted(Req, State) ->
    {[{{<<"application">>, <<"json">>, []}, put_json}], Req, State}.

content_types_provided(Req, State) ->
    {[{{<<"application">>, <<"json">>, []}, get_json}], Req, State}.

Now the request is sent to the function that handles the HTTP method type of the request.

For a POST, a request to create a new todo item, the function process_post/2 is sent the request. Here we retrieve the body, a JSON object, from the request, convert it to a record and save the model. We’ll see how this record conversion is done when we look at the model module. To inform the frontend of the id of our new resource we set the location header to be the path with the id.

process_post(Req, State) ->
    {ok, Body, Req1} = cowboy_http_req:body(Req),
    Todo = bcmvc_model_todo:to_record(Body),
    bcmvc_model_todo:save(Todo),

    NewId = bcmvc_model_todo:get(id, Todo),
    {ok, Req2} = cowboy_http_req:set_resp_header(
                   <<"Location">>, <<"/todos/", NewId/binary>>, Req1),

    {true, Req2, State}.

For this handler we expect PUT for an update to an object, that is what Batman.js does, but a PATCH would make more sense. For a PUT the URL contains the id for the todo item to be updated. That is retrieved with the binding/2 function. The todo record is created the same as in process_post/2 but then the this id is set for the model and the update/1 function is used to save it to the database.

put_json(Req, State) ->
    {ok, Body, Req1} = cowboy_http_req:body(Req),
    {TodoId, Req2} = cowboy_http_req:binding(todo, Req1),
    Todo = bcmvc_model_todo:to_record(Body),
    Todo2 = bcmvc_model_todo:set([{id, TodoId}], Todo),
    bcmvc_model_todo:update(Todo2),    
    {true, Req2, State}.

For a GET request, which for this application we do not deal with a request for a single todo item, all todo items are retrieved from the model module. Each of these is passed to the model’s to_json/1 function and the result of converting each to JSON is combined into a binary string and placed between brackets so the Batman.js frontend receives a proper JSON list of JSON objects.

get_json(Req, State) ->
    JsonModels = lists:foldr(fun(X, <<"">>) ->
                                 X;
                            (X, Acc) ->
                                 <<Acc/binary, ",", X/binary>>
                         end, <<"">>, [bcmvc_model_todo:to_json(Model) || Model <- bcmvc_model_todo:all()]),

    {<<"[", JsonModels/binary, "]">>, Req, State}.

And lastly, DELETE. Like in PUT the todo item’s id is retrieved from the bindings created based on the dispatch rules and this is passed to the model’s delete function.

delete_resource(Req, State) ->
    {TodoId, Req1} = cowboy_http_req:binding(todo, Req),
    bcmvc_model_todo:delete(TodoId),
    {true, Req1, State}.

Models

Model’s are repsented as records and must provide serialization functions to go between JSON and a record. Each model uses a parse transform that creates functions for creating and updating the record. The transform is a modified version of exprecs from Ulf Wiger that also uses the type definitions in the record to ensure when setting a field that it is the correct type. For example in the todo model isDone is a boolean, so when the model is created the boolean convert function will be matched to convert the string representation to an atom:

convert(boolean, <<"false">>) ->
    false;
convert(boolean, <<"true">>) ->
    true;

So the key pieces of the bcmvc_model_todo are:

-compile({parse_transform, bcmvc_model_transform}).

-record(bcmvc_model_todo, {id = ossp_uuid:make(v1, text) :: string(),
                           body                          :: binary(),
                           isDone                        :: boolean()}).

to_json(Record) ->
    ?record_to_json(?MODULE, Record).

to_record(JSON) ->
    ?json_to_record(?MODULE, JSON).

The ?record_to_json and ?json_to_record macros are defined in jsonerl.hrl. These marcos are generic and work for any record that is typed and uses the model transform.

Conclusion

Clearly, much of what the resource handler and model do is generic and can be abstracted out so that implementing new models and resources can be even simpler. This is the goal of my project Maru. Currently it is based on Webmachine but is now being convered to Cowboy.

In the end, using Cowboy for building a RESTful interface for your application allows you to build interfaces for the frontend entirely separted from backend development, and if you want multiple interfaces (like native mobile and web), they both talk directly to the same backend. Also, from the beginning you have the option to open up your application with an API for other developers to take your application new places, and, shameless plug here, add your API to Mashape to spread your new app!

Erlang, Cowboy and Batman.js for Building Web Applications

I’ll have a complete walk of through using Cowboy and Batman.js to build the TodoMVC clone in a few days. For now I have the slides from my talk at the Chicago Erlang User Group:

Chicago Erlang User Group April, 4th 2012

I couldn’t get iframe embedding to work with WordPress… So if anyone knows what that is up with please comment.

Opa Plugin Development with a PubNub Example

This will be a two part series of posts on writing plugins for Opa. One of Opa’s greatest features is you write everything in the functional, staticly typed Opa language. This even includes the frontend code you’d usually do in Javascript. This means your code is less error-prone which significantly reduces the amount of time spent on debugging code.

But how do you use Javascript library X within your Opa code?

While you can include Javascript code in a resource, like we do here for Google Analytics:

do Resource.register_external_js("/resources/js/google_analytics.js")

This isn’t what we want to do if we want to call external Javascript functions from Opa code. For this we create an Opa plugin. In this post I’ll be showing how I built the Opa client side bindings for the PubNub Javascript API, fork it on github and see all the other language bindings. PubNub is a cloud hosted real-time publish and subscribe interface for all types of applications.

Opa allows binding to external functions through its Binding System Library (BSL). In order to use this to create a plugin for some external Javascript, we create a file pubnub.js that we will compile to a plugin using opa-plugin-builder. Below is an example of one of the external functions that needs to be defined to use PubNub’s publish.

##register publish : string, string -> void
##args(c, m)
{
    PUBNUB.publish({
                       channel : c, message : m
                   });

    return js_void;
}

The first line registers the function publish and specifies its arguments to be two strings and its return value to be void. The next line names the arguments. You can think of ##args like Javascript’s function keyword. In the function, we can use the PUBNUB library just like we would normally. Here we pass to publish the channel and the message to be published. Note that the message can be any Javascript object, even though its a string to the plugin. We simply must serialize the JSON before passing to publish, which we’ll see shortly.

Now after defining external functions for all PubNub api calls in the pubnub.js file we can compile it:

$ opa-plugin-builder src/plugin/pubnub.js -o pubnub

This creates the plugin pubnub.opp.

To make using the plugin easier, we create a package in pubnub.opa that will contain a module PubNub. Below is the contents of pubnub.opa for the publish function that will show how to register the external functions and create a module named PubNub around them.

package pubnub

pubnub_publish = %%pubnub.publish%%

@client publish_client = pubnub_publish

PubNub = {{
  publish(channel: string, j : RPC.Json.json) =
    publish_client(channel, Json.to_string(j))
}}

do Resource.register_external_js("http://cdn.pubnub.com/pubnub-3.1.min.js")

The first thing to notice is setting the pubnub_publish variable. The %% syntax is specifying that pubnub.publish is the key of an external primitive. pubnub is the name of the plugin (pubnub.opp) and publish is the function we registered in the plugin.

After this we create another variable publish_client that specifies that that this function (variables can contain functions) is to be run on the client side. Lastly, we define a module PubNub and a publish function inside the module to be used by our Opa programs.

At the end we make sure that the needed PubNub Javascript file is includes in all resources by registering it and giving the URL to the PubNub CDN.

Now we can compile it all together with the opa-plugin-builder command from above and use the plugin.

import pubnub

broadcast(author, msg) =
   username : RPC.Json.json = {String = author}
   message : RPC.Json.json = {String = msg}
   record_json : RPC.Json.json = {Record = [("username", username), ("message", message)]}
   do PubNub.publish("chat", record_json)
   Dom.clear_value(#entry)

The broadcast function is part of pubnub_chat.opa. broadcast takes 2 arguments, the username of the user sending the message and the message itself. We then construct three variables of Opa’s json type, username, message and record_json. username and message are simply Json strings and they are then combined into a Json Record type that is a list of string/Json pairs. Since our PubNub.publish function converts the Opa Json to a string before sending to the Javascript binding we can simply pass the Opa Json variable record_json.

Lastly, lets take a quick look at the subscribe function. Defining it in pubnub.js look like:

##register subscribe : string, (string -> void) -> void

Since it takes a callback function you’ll notice that the second argument’s type looks different from in publish. (string -> void) is defining that the function takes a variable that is a function which takes a single argument of type string and has a void return type.

Now in pubnub.opa we have a subscribe function that is hardly different from the publish function we described above.

subscribe(channel: string, callback) =
  subscribe_client(channel, callback)

But how do we define a function we can pass to subscribe? That is the great thing, it can be any Opa function!

PubNub.subscribe("chat", (x -> user_update(x)))

Above we are subscribing to the channel chat and passing an anonymous function that takes a single argument and sends it to the function user_update.

user_update(x) =
  match Json.deserialize(x) with
         | {some={Record = [("username", username), ("message", message)]}} ->
                    line = <div class="line"> <div class="user">{Json.to_string(username)}:</>
                             <div class="message">{Json.to_string(message)}</>
                            </>
                    do Dom.transform([#conversation +<- line ])
                    Dom.scroll_to_bottom(#conversation)
         | _ -> void

Now every time a message is posted to the chat channel, our callback, the anonymous function, will be run and call the function user_update which we see deserializes the Json string to get an Opa Json Record. The rest is basic Opa DOM manipulation to add the new message to the messages in the conversation element.

In my next post on Opa and PubNub, I’ll be describing an Opa server side API for PubNub.

Adding JS To all Opa Resources: Use Case Google Analytics

I decided I wanted to add Google Analytics to OpaDo but had no idea how to easily tell each page to include the necessary Javascript. I asked on the Opa mailing list and got a quick and simple response. Frederic Ye pointed me to Resource.register_external_js

It couldn’t have been any easier. You simply place your google_analytics.js file in your project and use the Resource.register_external_js function to modify the default customization of all Resources. See code below or on the github repo.

package opado.main

import opado.user
import opado.admin
import opado.todo

urls : Parser.general_parser(http_request -> resource) =
  parser
  | {Rule.debug_parse_string(s -> Log.notice("URL",s))} Rule.fail -> error("")
  | "/todos" result={Todo.resource} -> result
  | "/user" result={User.resource} -> result
  | "/login" result={User.resource} -> result
  | "/admin" result={Admin.resource} -> result
  | (.*) result={Todo.resource} -> result

do Resource.register_external_js("/resources/js/google_analytics.js")
server = Server.of_bundle([@static_resource_directory("resources")])
server = Server.make(urls)

For a longer article/tutorial on dealing with external resources check out this blog post from the Opa team Dealing with External Resources.

TodoMVC in Opa

Edit: I just learned that dotcloud supports Opa! So I’ve pushed OpaDo and you can see a demo here http://opado-tristan.sloughter.dotcloud.com/

I wanted something quick and simple to do in Opa to give it a try so I decided to implement the TodoMVC example that has been redone in almost all Javascript frameworks, https://github.com/addyosmani/todomvc.

The code can be found on GitHub here: https://github.com/tsloughter/OpaDo

Opa is unique in that it is not only a new language but also a new web server and database. While Opa’s page pushes the idea that its for the cloud and its easy distribution, I found the nicest part being the static typing and no need for Javascript.

The functions below handle interactions with the Todo items. It somewhat reminds me of Lift but taken even farther.

/** * {1 User interface} */
update_counts() =
  num_done = Dom.length(Dom.select_class("done"))
  total = Dom.length(Dom.select_class("todo"))
  do Dom.set_text(#number_done, Int.to_string(num_done))
  Dom.set_text(#number_left, Int.to_string(total - num_done))

make_done(id: string) =
  do if Dom.is_checked(Dom.select_inside(#{id}, Dom.select_raw("input"))) then Dom.add_class(#{id}, "done")
  else
    Dom.remove_class(#{id}, "done")

  update_counts()

remove_item(id: string) =
  do Dom.remove(#{id})
  update_counts()

remove_all_done() =
  do Dom.remove(Dom.select_parent_one(Dom.select_class("done")))
  update_counts()

add_todo(x: string) =
  id = Random.string(8)
  li_id = Random.string(8)
  line = <li id={ li_id }><div class="todo" id={ id }> <div class="display"> <input class="check" type="checkbox" onclick={_ -> make_done(id) } /> <div class="todo_content">{ x }</div> <span class="todo_destroy" onclick={_ -> remove_item(li_id) }></span> </div> <div class="edit"> <input class="todo-input" type="text" value="" /> </div> </div></li>
  do Dom.transform([#todo_list +<- line ])
  do Dom.scroll_to_bottom(#todo_list)
  do Dom.set_value(#new_todo, "")
  update_counts()

It is unique in combining the HTML into the language itself. Some have argued against this but when it works well it makes perfect sense. I don’t want to have to convert a designers HTML into some other representation! And being able to have type checked dynamic functionality within the HTML is a boon. Even with just this simple program, I found the usefulness of the type checker outstanding.

Next we have the main outline of the page and the entry part for the program.

start() =
  <div id="todoapp"> <div class="title"> <h1>Todos</h1> </div>
    <div class="content"> <div id=#create_todo> <input id=#new_todo placeholder="What needs to be done?" type="text" onnewline={_ -> add_todo(Dom.get_value(#new_todo)) } /> </div>
      <div id=#todos> <ul id=#todo_list></ul> </div>

      <div id="todo_stats"> <span class="todo_count"> <span id=#number_left class="number">0</span> <span class="word">items</span> left. </span> <span class="todo_clear"> <a href="#" onclick={_ -> remove_all_done() }> Clear <span id=#number_done class="number-done">0</span> completed <span class="word-done">items</span> </a> </span> </div>
    </div>
  </div>

/** * {1 Application} */

/** * Main entry point. */
server = Server.one_page_bundle("Todo",
       [@static_resource_directory("resources")],
       ["resources/todos.css"], start)

It won’t be able to replace my use of Erlang for the backend and Coffeescript for the frontend, but it looks very promising.

I’ll be extending this example to include persistence, sessions and users and will add posts as I complete those.

Batman.js vs Knockout.js

The following is NOT a tutorial for either Batman.js or Knockout.js. But, it is instead a sort of side-by-side comparison of the two for creating a user creation form that POSTs the new user’s data as JSON to the backend.

The method of web development I’ve come to find the best is based on heavy frontend Javascript (though written in Coffeescript) communicating with a backend via a RESTful interface. This is appealing, because you are not cluttering the application logic with view related code. This allows me to use Erlang, my language of choice, which is great at implementing RESTful interfaces with Webmachine, but not too great for trying to build a site like you would with Rails.

I recently began using Knockout.js and found it to be a great fit for my development paradigm. When Batman.js came out, I saw that it could provide me with what Knockout.js does but also already take care of pieces I would develop myself on the frontend, like RESTful persistence.

First, we have the Knockout.js logic, written in Coffeescript, for setting up a User class and object that observers the input fields.

class @User
  constructor : ->
    firstname : ko.observable ""
    lastname : ko.observable ""
    email : ko.observable ""
    username : ko.observable ""
    password : ko.observable ""

  save : ->
    if $("form").validate().form()
      $.post('/user', ko.toJSON(this), (data) -> window.location = "/login.html"; return false ;).error () -> alert("error"); return false;

user = new User
ko.applyBindings user

Now for the HTML for displaying the fields, configuring the submit handler, binding the inputs to Knockout.js observables and setting validators.

<form data-bind="submit: save">
    <div class="new_user_box">
    <label>First Name: </label>
    <input type=text name=firstname data-bind="value: firstname" minlength=2 maxlength=25 class="required" />

    <label>Last Name: </label>
    <input type=text name=lastname data-bind="value: lastname"  minlength=2 maxlength=25 class="required" />

    <label>Username: </label>
    <input type=text name=username data-bind="value: username" remote="/user/check"  minlength=6 maxlength=25 class="required" />

    <label>Password: </label>
    <input type=password id="password1" data-bind="value: password, uniqueName: true" minlength=8 class="required password" />

    <label>Retype Password: </label>
    <input type=password data-bind="uniqueName: true"  id="password1" equalto="#password1" class="required" />

    <label>Email Address: </label>
    <input type=email name=email data-bind="value: email"  remote="/user/email_check"  class="required email" />

    <input type=submit value="Save" id="saveSubmit" />
    </div>
</form>

With Batman.js our validation will be configured in the user model. Here we only need to set @persist to Batman.RestStorage and when a model is saved with the save method it will POST the encoded fields as JSON to /users.

class CT extends Batman.App
    @global yes
    @root 'users#index'

class CT.User extends Batman.Model
    @global yes
    @persist Batman.RestStorage
    @encode 'firstname', 'lastname', 'username', 'email', 'password'
    @validate 'firstname', presence: yes, maxLength: 255
    @validate 'lastname', presence: yes, maxLength: 255
    @validate 'username', presence: yes, lengthWithin: [6,255]
    @validate 'email', presence: yes
    @validate 'password', 'passwordConfirmation', presence: yes, lengthWithin: [6,255]

class CT.UsersController extends Batman.Controller
    user: null

    index: ->
        @set 'user', new User
        return false

    create: =>
        @user.save()
        return false

CT.run()

We see below that the Batman.js HTML is a bit cleaner than that in the Knockout.js example above.

        <form data-formfor-user="controllers.users.user" data-event-submit="controllers.users.create">
          <div class="new_user_box">
          <label>First Name: </label>
          <input type=text name=firstname data-bind="user.firstname" />

          <label>Last Name: </label>
          <input type=text name=lastname data-bind="user.lastname" />

          <label>Username: </label>
          <input type=text name=username data-bind="user.username" remote="/user/check" />

          <label>Password: </label>
          <input type=password data-bind="user.password" />

          <label>Retype Password: </label>
          <input type=password data-bind="user.passwordConfirmation" />

          <label>Email Address: </label>
          <input type=email data-bind="user.email" remote="/user/email_check" />

          <input type=submit value="Save" id="saveSubmit" />
          </div>

        </form>

eCloudEdit: Erlang, Webmachine and Backbone.js

To experiment with using a pure client-side rendering talking to an Erlang backend I’ve taken James Yu’s CloudEdit tutorial an app written with Backbone.js and Rails and converted the backend to use Webmachine and CouchDB. You can see eCloudEdit in action here. The Backbone.js code is the same so to understand that please see James’ post, here I’ll describe the Erlang backend.

To begin with we setup two applications, one for handling the web interaction and a second for handling the database interaction. We end up with this directory layout:

  |-eCloudEdit
   |-bin
   |-config
   |-doc
   |-lib
   |---ece_db
   |-----doc
   |-----ebin
   |-----include
   |-----src
   |---ece_web
   |-----doc
   |-----ebin
   |-----include
   |-----priv
   |-------images
   |-------javascripts
   |---------controllers
   |---------models
   |---------views
   |-------stylesheets
   |-----src

Under ece_web/priv is where all the html and Javascript from CloudEdit is placed, with nothing changed. To serve up the static content through Webmachine we use this resource which we won’t worry about in this article. Lets look at how webmachine deciced where to route requests. This is handled by a set of dispatch rules that are passed into Webmachine on startup. We can see how this is done by looking at ece_web_sup and how the supervisor loads Webmachine:

-spec init(list()) -> {ok, {SupFlags::any(), [ChildSpec::any()]}} |
                          ignore | {error, Reason::any()}.
init([]) ->
    WebChild = {webmachine_mochiweb,
                {webmachine_mochiweb, start, [config()]},
                permanent, 5000, worker, dynamic},

    RestartStrategy = one_for_one,
    MaxRestarts = 3,
    MaxSecondsBetweenRestarts = 10,
    SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},

    {ok, {SupFlags , [WebChild]}}.

config() ->
    {ok, IP} = application:get_env(webmachine_ip),
    {ok, Port} = application:get_env(webmachine_port),
    {ok, App}= application:get_application(),
    LogDir = code:priv_dir(App) ++ "/logs",
    {ok, Dispatch} = file:consult(filename:join([code:priv_dir(App), "dispatch"])),

    [{ip, IP},
     {port, Port},
     {log_dir, LogDir},
     {backlog, 128},
     {dispatch, Dispatch}].

The dispatch terms are loaded from a file dispatch in the application’s priv directory. For this app the dispatch file contains:

{["documents", id], ece_resource_documents, []}.
{["documents"], ece_resource_documents, []}.
{['*'], ece_resource_static, []}.

There are two resources, one for handling the requests for creating, updating and viewing documents and one for serving up all other requests (static html, js and css files). The first rule matches paths like, /documents/foo, id is set to foo and the request is sent to the documents resource. If there is nothing after /documents it is still sent to the documents resource but there is no id.

Webmachine is essentially a REST toolkit. You build resources by defining functions that handle the different possible HTTP requests. For this webapp we’ll only be dealing with GET, POST and PUT. GET is used if we’d like to retrieve information on documents, POST is for creating a new document and PUT is for updating a document.

-record(ctx, {db}).

init([]) ->
    {ok, PID} = ece_db_sup:start_child(),
    {ok, #ctx{db=PID}}.

allowed_methods(ReqData, Ctx) ->
    {['HEAD', 'GET', 'POST', 'PUT'], ReqData, Ctx}.

content_types_accepted(ReqData, Ctx) ->
    {[{"application/json", from_json}], ReqData, Ctx}.

content_types_provided(ReqData, Ctx) ->
    {[{"application/json", to_json}], ReqData, Ctx}.

finish_request(ReqData, Ctx) ->
    ece_db_sup:terminate_child(Ctx#ctx.db),
    {true, ReqData, Ctx}.

To handle GET requests we implemented the to_json function we specified in content_types_provided to handle GET requests for json data.

to_json(ReqData, Ctx) ->
    case wrq:path_info(id, ReqData) of
        undefined ->
            All = ece_db:all(Ctx#ctx.db),
            {All, ReqData, Ctx};
        ID ->
            JsonDoc = ece_db:find(Ctx#ctx.db, ID),
            {JsonDoc, ReqData, Ctx}
    end.

wrq:path_info is used to find the value of id, which we set in the dispatch file, if it is undefined we know the request is for all documents, while if it has a value we know to find the contents of the document with that id and return its contents. We’ll see the content of ece_db:all/1 and ece_db:find/2 in the next article. Just know they both return JSON data structures.

Now we must support POST for creating documents or we have nothing to return to a GET request.

process_post(ReqData, Ctx) ->
    [{JsonDoc, _}] = mochiweb_util:parse_qs(wrq:req_body(ReqData)),
    {struct, Doc} = mochijson2:decode(JsonDoc),
    NewDoc = ece_db:create(Ctx#ctx.db, {Doc}),
    ReqData2 = wrq:set_resp_body(NewDoc, ReqData),
    {true, ReqData2, Ctx}.

wrq:req_body/1 returns the contents of the body sent in the HTTP request. Here it is the conents of the document to store. We decode it to an Erlang data structure and pass it to the ece_db app for inserting into the database. After inserting to the database the create/2 function returns the new document with a new element id (in this case generated by CouchDB). This is required so we know the document’s id which is used by the Backbone.js frontend. In order to return it from the POST request we must set response body to the contents of the document with wrq:set_resp_body/2

Lastly, updating documents requires support for PUT. In contents_type_accepted/2 we’ve specified that PUT requests with JSON content is sent to the function from_json/2:

from_json(ReqData, Ctx) ->
    case wrq:path_info(id, ReqData) of
        undefined ->
            {false, ReqData, Ctx};
        ID ->
            JsonDoc = wrq:req_body(ReqData),
            {struct, Doc} = mochijson2:decode(JsonDoc),
            NewDoc = ece_db:update(Ctx#ctx.db, ID, Doc),
            ReqData2 = wrq:set_resp_body(NewDoc, ReqData),
            {true, ReqData2, Ctx}
    end.

If this request was not routed through the first rule in our dispatch file it does not have an id and thus can not be an update. When this happens we return false so the frontend is aware something has gone wrong. For requests containing an id we pass the contents of the requests body to ece_db‘s update/2 function.

In the next post I’ll show how ece_db is implemented with Couchbeam for reading and writing the documents to CouchDB on Cloudant.