Macros and functions for routing apps and services in LFE+Yaws
REST is a party, and you know it.
The name started with more than just a party reference: the intent was for this library to be used to develop RESTful services more easily by having one's routes more clear: LFe rEpresentational State Transfer.
But the web is more than REST, so everybody gets to play. The name's staying, though.
This project assumes that you have rebar
and lfetool installed somwhere in your $PATH
.
This project depends upon the following, which are automatically installed
to the deps
directory of this project when you run make compile
:
Just add it to your rebar.config
deps:
{deps, [
...
{lfest, ".*", {git, "[email protected]:lfex/lfest.git", "master"}}
]}.
If you have created your project with lfetool
, you can download
lfeest
with the following:
$ make get-deps
Or, you can have it download automatically when you compile:
$ make compile
Create your application/service routes with the (defroutes ...)
form.
Here is an example:
(include-lib "deps/lfest/include/macros.lfe")
(defroutes
;; top-level
('GET "/"
(lfest-html-resp:ok "Welcome to the Volvo Store!"))
;; single order operations
('POST "/order"
(create-order (lfest:get-data arg-data)))
('GET "/order/:id"
(get-order id))
('PUT "/order/:id"
(update-order id (lfest:get-data arg-data)))
('DELETE "/order/:id"
(delete-order id))
;; order collection operations
('GET "/orders"
(get-orders))
;; payment operations
('GET "/payment/order/:id"
(get-payment-status id))
('PUT "/payment/order/:id"
(make-payment id (lfest:get-data arg-data)))
;; error conditions
('ALLOWONLY
('GET 'POST 'PUT 'DELETE)
(lfest-json-resp:method-not-allowed))
('NOTFOUND
(lfest-json-resp:not-found "Bad path: invalid operation.")))
Note that this creates a routes/3
function which can then be called
in the out/1
function that is required of a YAWS appmod module.
For an example of this in action, see this mini REST-api.
A few important things to note here:)
- Each route is composed of an HTTP verb, a path, and a function to execute should both the verb and path match.
- The function call in the route has access to the
arg-data
passed from YAWS; this contains all the data you could conceivably need to process a request. (You may need to import theyaws_api.hrl
in your module to parse the data of your choice, though.) - If a path has a segment preceded by a colon, this will be converted to a
variable by the
(defroutes ...)
macro; the variable will then be accessible from the function you provide in that route. - The
(defroutes ...)
macro generates theroutes/3
function; it's three arguments are the HTTP verb (method name), the path info (a list of path segments, with the":varname"
segments converted tovarname
/ variable segments), and then thearg-data
variable from YAWS.
lfest needs to provide YAWS with an out/1
function. The location of this
function is configured in your etc/yaws.conf
file in the
<appmods ...>
directives (it can be repeated for supporting multiple
endpoints).
YAWS will call this function with one argument: the YAWS arg
record
data. Since this function is the entry point for applications running under
YAWS, it is responsible for determining how to process all requests.
The out/1
function in lfest-based apps calls the routes/3
function
generated by the (defroutes ...)
mamcro.
The route definition macro does some pretty heavy remixing of the routes
defined in (defroutes ...)
. The route definition given in the "Usage"
section above actually expands to the following LFE before being compiled to
a .beam
:
#((define-function routes
(match-lambda
(('GET () arg-data)
(call 'lfest-html-resp 'ok "Welcome to the Volvo Store!"))
(('POST ("order") arg-data)
(create-order (call 'lfest 'get-data arg-data)))
(('GET ("order" id) arg-data) (get-order id))
(('PUT ("order" id) arg-data)
(update-order id (call 'lfest 'get-data arg-data)))
(('DELETE ("order" id) arg-data) (delete-order id))
(('GET ("orders") arg-data) (get-orders))
(('GET ("payment" "order" id) arg-data) (get-payment-status id))
(('PUT ("payment" "order" id) arg-data)
(make-payment id (call 'lfest 'get-data arg-data)))
((method p a)
(when
(not
(if (call 'erlang '=:= 'GET method)
'true
(if (call 'erlang '=:= 'POST method)
'true
(if (call 'erlang '=:= 'PUT method)
'true
(call 'erlang '=:= 'DELETE method))))))
(call 'lfest-json-resp 'method-not-allowed))
((method path arg-data)
(call 'lfest-json-resp 'not-found "Bad path: invalid operation."))))
6)
When it is compiled, the routes/3
function is available for use from
wherever you have defined your routes.
License ↟
Apache Version 2 License
Copyright © 2014-2019, Duncan McGreggor [email protected]
[](Named page links below ...)