tiny-routes
2024-10-12
A tiny routing library for Common Lisp targeting Clack.
0.1Introduction
TINY-ROUTES is a bare-bones routing library for Common Lisptargeting the Clack web application development. Its only externaldependency is CL-PPCRE which is used to match and bind dynamic URLparameters.0.2Prerequisites
The following lists the prerequisites needed to follow thisREADME 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 throughits documentation.
0.3Motivation
Clack's killer feature is that it allows us server-agnostic webapplication. Namely, web applications that are not tied to aspecific 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.
0.4Definitions
The following represents definitions used in this document.request
orenvironemnt
- A plist representing an HTTPrequest. 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 newfunctionality.
response
- A list or lambda representing an HTTP response. Perthe 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 asits first argument, optionally accepts other arguments, andreturns a potentially different handler.
0.5Usage
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 astring containing the bound value =account-
- A catch-all handler which returns an HTTP 404 response with thebody
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 handlersand returns a new handler that loops through each handler andreturns the first non-nil response it finds. - The
define-get
macro creates a handler that matches on HTTP GETrequests. The macro accepts apath-template
and a requestbinding. Please note TINY-ROUTES also exposes similar macros forPOST, PUT, HEADER, HEAD, and OPTIONS. - The
ok
function accepts an optional body and returns aresponse list with HTTP status 200 OK. - The
define-get
macro now receives a path-template with adynamic parameter named:account-id
. This value associatedwith this dynamic parameter is made available as part of therequest's path-params as seen in line (5). - The
path-param
selector function can be used to quickly parsea path param from a request object. - We leverage the
format
function to show that the response bodycan also be dynamic. - The
define-any
macro can be used to implement catch allroutes. - The
not-found
function accepts an optional body and returns aresponse list with HTTP status 404 Not Found.
0.6Installation
To install via quicklisp please run the following in your running REPL:(ql:quickload "tiny-routes")
0.7Sample code
Please see tiny-routes-realworld-example-app for a full-fledgedClack REST application demonstrating one possible use oftiny-routes.Enjoy!