Skip to content

A tiny routing library for Common Lisp targeting Clack.

Notifications You must be signed in to change notification settings

jeko2000/tiny-routes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TINY-ROUTES

Introduction

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.

Prerequisites

The following lists the prerequisites needed to follow this README successfully.

  1. Common Lisp web frameworks
  2. HTTP
  3. Quicklisp

Motivation

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.

Definitions

The following represents definitions used in this document.

request or environemnt
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:
  1. A list of three elements representing an HTTP status code, HTTP headers, and a response body
  2. A lambda accepting a response function to be called with a response asynchronously.
handler
A function that accepts a request and returns a response or nil.
middleware
A combinator function that accepts a handler as its first argument, optionally accepts other arguments, and returns a potentially different handler.

Usage

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

  1. 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.
  2. The define-get macro creates a handler that matches on HTTP GET requests. The macro accepts a path-template and a request binding. Please note TINY-ROUTES also exposes similar macros for POST, PUT, HEADER, HEAD, and OPTIONS.
  3. The ok function accepts an optional body and returns a response list with HTTP status 200 OK.
  4. 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).
  5. The path-param selector function can be used to quickly parse a path param from a request object.
  6. We leverage the format function to show that the response body can also be dynamic.
  7. The define-any macro can be used to implement catch all routes.
  8. The not-found function accepts an optional body and returns a response list with HTTP status 404 Not Found.

Installation

To install via quicklisp please run the following in your running REPL:

(ql:quickload "tiny-routes")

Sample code

Please see tiny-routes-realworld-example-app for a full-fledged Clack REST application demonstrating one possible use of tiny-routes.

Enjoy!