Skip to content

Tutorial for creating a RESTful JSON API with Mnesia, Cowboy WebServer, and JSX

License

Notifications You must be signed in to change notification settings

AustinElixir/RESTapi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RESTapi

This is a bare bones project that includes a very simple database interface for a collection of Mnesia tables.

The intent of this project is to provide a framework to quickly build a small HTTPrest API on top of Mnesia.

Follow these steps to complete the tutorial.

Please note that we'll be doing some things you might not want in production as purely educational exercises.

Setup Guide

Dowload Project Stucture

  • git clone https://github.com/austinerlang/RESTapi

Setup Virtual Dev Environment

DOCKER

Make sure you have docker installed

  • sudo docker run -it --rm -p 8080:8080 austinerlang/restapi /bin/bash
VAGRANT

Make sure you have vagrant installed

  • vagrant up
  • vagrant ssh

Mnesia DB

Verify Mnesia built the schema files correctly.

  • ls -l /var/rest_db/

If the directory is empty, there is a bug with vagrant / erlang shell during post provisioning. Manually fix:

  • cd /vagrant/rest
  • rebar3 release
  • erl -pa _build/default/lib/*/ebin -sname rest -mnesia dir '"/var/rest_db"' -run rest_db initial_setup -s init stop

Now verify the files exist in /var/rest_db.

Tutorial

Checkout tutorial branch

  • git fetch
  • git checkout tutorial

Build and run release

  • cd /vagrant/rest
  • rebar3 release
  • rebar3 run

Play with the Mnesia Database using provided interface

rest_db:create_employee("Johnny", "William", "Carson").
rest_db:get_employee(Id).
rest_db:update_employee(Id, First, Middle, Last).
rest_db:delete_employee(Id).

Ensure you have some entries and make a note of the ID numbers.

Create a gen server from your template with these callbacks

rest/apps/rest/src/rest_api.erl

handle_call({get_emp, ID}, _From, State) ->
	{ok, Emp} = rest_db:get_employee(ID),
    Reply = {ok, jsx:encode(Emp)},
    {reply, Reply, State};

handle_call({update_emp, ID, Emp}, _From, State) ->
	EmpDecoded = jsx:decode(Emp, [return_maps]),
	First = maps:get(<<"first">>, EmpDecoded),
	Middle = maps:get(<<"middle">>, EmpDecoded),
	Last = maps:get(<<"last">>, EmpDecoded),
	{ok, EmpNew} = rest_db:update_employee(ID, First, Middle, Last),
    Reply = {ok, jsx:encode(EmpNew)},
    {reply, Reply, State};

Supervise the gen_server

rest/apps/rest/src/rest_sup.erl

		#{ id => rest_api,
           start => {rest_api, start_link, []},
           restart => permanent,
           shutdown => 1000,
           type => worker }

Add HTTP routing

Add your routing to your application in: rest_app.erl

	Dispatch = cowboy_router:compile([
	    %% {HostMatch, list({PathMatch, Handler, Opts})}
	    {'_', [
	    	{"/get_employee/:empid", handler_emp_get, []},
	    	{"/set_employee/:empid", handler_emp_set, []}
	    ]}
	]),
	%% Name, NbAcceptors, TransOpts, ProtoOpts
	cowboy:start_http(my_http_listener, 100,
	    [{port, 8080}],
	    [{env, [{dispatch, Dispatch}]}]
	),

Create your handlers

rest/apps/rest/src/handler_emp_get.erl

-module(handler_emp_get).
-behaviour(cowboy_http_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

init(_Type, Req, _State) ->
	{ok, Req, []}.

handle(Req, _State) ->
	{ID, _Req} = cowboy_req:binding(empid, Req),

	{ok, Payload} = gen_server:call(rest_api, {get_emp, binary_to_integer(ID)}),
	{ok, Req2} = cowboy_req:reply(200,
			        [{<<"content-type">>, <<"application/json">>}],
			        Payload,
			        Req
			     ),
	{ok, Req2, []}.

terminate(_Reason, _Req, _State) ->
	ok.

rest/apps/rest/src/handler_emp_set.erl

-module(handler_emp_set).
-behaviour(cowboy_http_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

init(_Type, Req, _State) ->
	{ok, Req, []}.

handle(Req, _State) ->
	{ID, _Req} = cowboy_req:binding(empid, Req),
	respond(Req, ID, cowboy_req:has_body(Req)).

respond(Req, ID, true) ->
	{ok, Body, _Req} = cowboy_req:body(Req),
	io:format("BODY: ~p~n", [Body]),
	{ok, Payload} = gen_server:call(rest_api, {update_emp, binary_to_integer(ID), Body}),
	{ok, ReqNew} = cowboy_req:reply(200,
			        [{<<"content-type">>, <<"application/json">>},
			         {<<"Access-Control-Allow-Origin">>, <<"*">>}], % chrome security
			        Payload,
			        Req
			     ),
	{ok, ReqNew, []};

respond(Req, ID, false) ->
	{ok, Payload} = gen_server:call(rest_api, {get_emp, binary_to_integer(ID)}),
	{ok, ReqNew} = cowboy_req:reply(200,
			        [{<<"content-type">>, <<"application/json">>}],
			        Payload,
			        Req
			     ),
	{ok, ReqNew, []}.

terminate(_Reason, _Req, _State) ->
	ok.

Test HTTP API Calls

  • curl http://localhost:8080/get_employee/<ID>
  • curl -H "Content-Type: application/json" -X POST -d '{ "first": "FirstName", "middle": "MiddleName", "last": "LastName" }' http://localhost:8080/set_employee/<ID>

About

Tutorial for creating a RESTful JSON API with Mnesia, Cowboy WebServer, and JSX

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published