TINY-ROUTES is a bare-bones routing library for Common Lisp targeting the Clack web application development. Its only external dependency is CL-PPCRE which is used to match and bind dynamic URL parameters.
The following lists the prerequisites needed to follow this README successfully.
- Common Lisp web frameworks
- In particular, Hunchentoot and Clack.
- HTTP
- Specifically, you should be familiar with HTTP methods.
- Quicklisp
- Ideally, you should have Quicklisp installed and read through its documentation.
Clack’s killer feature is that it allows us server-agnostic web application. Namely, web applications that are not tied to a specific server library such as Hunchentoot, Toot, or Wookie.
Clack distills a web application down to just a handler
function
that accepts a request
(or an environment
) object (i.e., a
plist containing data like HTTP request method, request URI, among
others) and returns an appropriate HTTP response. This HTTP
response is represented simply by a list of three elements
containing the HTTP status code, HTTP headers, and a response body.
This simplification provides developers with a great amount of
flexibility given that we can leverage the entire power of lisp to
create these simple list structures representing HTTP responses.
However, I quickly found that Clack’ itself does not offer tools or
guidance on how such request handler
functions should be written,
composed, and ultimately managed.
The purpose of TINY-ROUTES is to provide a small set of helper/utility functions and macros to aid the developer in building clack applications by breaking them up into composable request handlers and middleware functions.
The following represents definitions used in this document.
request
orenvironemnt
- A plist representing an HTTP request. Please see Clack for a list of supported fields. Finally, please note that users and/or middleware are allowed (and encouraged!) to add new fields to the request object for new functionality.
response
- A list or lambda representing an HTTP response. Per
the Clack documentation, a response can either be:
- A list of three elements representing an HTTP status code, HTTP headers, and a response body
- A lambda accepting a
response
function to be called with aresponse
asynchronously.
handler
- A function that accepts a
request
and returns aresponse
or nil. middleware
- A combinator function that accepts a handler as its first argument, optionally accepts other arguments, and returns a potentially different handler.
The following application exposes:
- A static endpoint which returns an HTTP 200 response with the body
alive
- A dynamic endpoint which returns an HTTP 200 response with a string containing the bound value =account-
- A catch-all handler which returns an HTTP 404 response with the
body
not-found
.
(define-routes *app* ; (1)
(define-get "/" () ; (2)
(ok "alive")) ; (3)
(define-get "/accounts/:account-id" (request) ; (4)
(let ((account-id (path-parameter request :account-id))) ; (5)
(ok (format nil "Your account id: ~a." account-id)))) ; (6)
(define-any "*" () ; (7)
(not-found "not-found"))) ; (8)
Each line in the example is detailed below
- The
define-routes
macro accepts a variable number of handlers and returns a new handler that loops through each handler and returns the first non-nil response it finds. - The
define-get
macro creates a handler that matches on HTTP GET requests. The macro accepts apath-template
and a request binding. Please note TINY-ROUTES also exposes similar macros for POST, PUT, HEADER, HEAD, and OPTIONS. - The
ok
function accepts an optional body and returns a response list with HTTP status 200 OK. - The
define-get
macro now receives a path-template with a dynamic parameter named:account-id
. This value associated with this dynamic parameter is made available as part of the request’s path-params as seen in line (5). - The
path-param
selector function can be used to quickly parse a path param from a request object. - We leverage the
format
function to show that the response body can also be dynamic. - The
define-any
macro can be used to implement catch all routes. - The
not-found
function accepts an optional body and returns a response list with HTTP status 404 Not Found.
To install via quicklisp please run the following in your running REPL:
(ql:quickload "tiny-routes")
Please see tiny-routes-realworld-example-app for a full-fledged Clack REST application demonstrating one possible use of tiny-routes.
Enjoy!