The jingle Reference Manual

This is the jingle Reference Manual, version 0.1.0, generated automatically by Declt version 4.0 beta 2 "William Riker" on Wed Mar 15 05:01:57 2023 GMT+0.

Table of Contents


1 Introduction


2 Systems

The main system appears first, followed by any subsystem dependency.


2.1 jingle

jingle – ningle with bells and whistles

Long Name

jingle

Maintainer

Marin Atanasov Nikolov <>

Author

Marin Atanasov Nikolov <>

Home Page

https://github.com/dnaeon/cl-jingle

Source Control

https://github.com/dnaeon/cl-jingle

Bug Tracker

https://github.com/dnaeon/cl-jingle

License

BSD 2-Clause

Long Description

* jingle

=jingle= is [[https://github.com/fukamachi/ningle][ningle]], but with bells and whistles.

* Requirements

- [[https://www.quicklisp.org/beta/][Quicklisp]]

* Installation

=jingle= is not yet in Quicklisp, so in order to install it you will need to
clone the repo and add it to your [[https://www.quicklisp.org/beta/faq.html][Quicklisp local-projects]].

#+begin_src shell
cd ~/quicklisp/local-projects
git clone https://github.com/dnaeon/cl-jingle.git
#+end_src

* Demo

The =JINGLE.DEMO= system provides a ready-to-run example REST API,
which comes with an [[https://swagger.io/specification/][OpenAPI 3.x spec]], [[https://swagger.io/tools/swagger-ui/][Swagger UI]], and a command-line interface app built with [[https://github.com/dnaeon/clingon][clingon]].

In order to build the demo application, simply execute this command.

#+begin_src shell
make demo
#+end_src

This will build the =jingle-demo= app, which you can find in the
=bin/= directory.

In order to start the HTTP server, execute the following command.

#+begin_src shell
bin/jingle-demo serve
#+end_src

Once the HTTP server is up and running navigate to
[[http://localhost:5000/api/docs/][http://localhost:5000/api/docs/]] which should take you to the Swagger
UI.

[[./images/jingle-swagger-ui.png]]

Make sure to check the various sub-commands provided by =jingle-demo=,
which allow you to interface with the REST APIs.

You can also run the demo app in Docker. First, build the image.

#+begin_src shell
make demo-docker
#+end_src

Once the image is built you can start up the service by executing the
following command.

#+begin_src shell
docker run -p 5000:5000 cl-jingle:latest serve –address 0.0.0.0
#+end_src

You can also view a short demo of the command-line interface
application, which interfaces with the REST API here.

[[./images/jingle-demo.gif]]

* Usage

Start up your REPL and load the =JINGLE= system.

#+begin_src lisp
CL-USER> (ql:quickload :jingle)
To load "jingle":
Load 1 ASDF system:
jingle
; Loading "jingle"
..................................................
[package jingle.core].............................
[package jingle]

(:JINGLE)
#+end_src

First thing we need to do is to create a new =JINGLE:APP= instance.

#+begin_src lisp
CL-USER> (defparameter *app* (jingle:make-app))
*APP*
#+end_src

When creating a new instance of =JINGLE:APP= you can provide
additional keyword args, which specify what HTTP server to use,
address to bind to, the port to listen on, middlewares, etc..

A very simple HTTP handler, which returns =Hello, World!= looks like
this.

#+begin_src lisp
(defun hello-handler (params)
(declare (ignore params))
"Hello, World!")
#+end_src

The following is an example of an HTTP handler which echoes back the
payload you send to it.

#+begin_src lisp
(defun echo-handler (params)
"A simple handler which echoes back the payload you send to it"
(declare (ignore params))
(jingle:set-response-header :content-type (jingle:request-content-type jingle:*request*))
(jingle:set-response-header :content-length (jingle:request-content-length jingle:*request*))
(maphash (lambda (k v)
(jingle:set-response-header k v))
(jingle:request-headers jingle:*request*))
(jingle:request-content jingle:*request*))
#+end_src

Next thing we need to do is register our handlers.

#+begin_src lisp
CL-USER> (setf (jingle:route *app* "/hello") #’hello-handler)
CL-USER> (setf (jingle:route *app* "/echo" :method :post) #’echo-handler)
#+end_src

And now we can start the app.

#+begin_src lisp
CL-USER> (jingle:start *app*)
#+end_src

Trying out our endpoints using =curl(1)= gives us this result.

#+begin_src shell
$ curl -vvv –get http://localhost:5000/hello
* Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /hello HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 09 Dec 2022 09:46:13 GMT
< Server: Hunchentoot 1.3.0
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=utf-8
<
* Connection #0 to host localhost left intact
Hello, World!
#+end_src

And this is our echo handler.

#+begin_src shell
$ curl -v -s –data ’{"foo": "bar", "baz": "42"}’ -H "My-Header: SomeValue" -H "Content-Type: application/json" -X POST http://localhost:5000/echo * Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> POST /echo HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.86.0
> Accept: */*
> My-Header: SomeValue
> Content-Type: application/json
> Content-Length: 27
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 09 Dec 2022 13:57:30 GMT
< Server: Hunchentoot 1.3.0
< My-Header: SomeValue
< Accept: */*
< User-Agent: curl/7.86.0
< Host: localhost:5000
< Content-Length: 27
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
{"foo": "bar", "baz": "42"}
#+end_src

In order to stop the application, evaluate the following expression.

#+begin_src lisp
CL-USER> (jingle:stop *app*)
#+end_src

** Handlers

Handlers are regular [[https://github.com/fukamachi/ningle][ningle]] routes, which accept a single argument,
representing the request parameters.

** Environment

=jingle= exports the special variable =JINGLE:*ENV*= which is
dynamically bound to the request environment of [[https://github.com/fukamachi/lack][Lack]]. You can query
the environment directly from =jingle= and don’t have to worry about
where the environment is coming from.

** Headers

=jingle= provides the =JINGLE:SET-RESPONSE-HEADER= function for
setting up HTTP response headers.

A simple handler which sets the =Content-Type= header to =text/plain=
looks like this.

#+begin_src lisp
(defun hello (params)
(declare (ignore params))
(jingle:set-response-header :content-type "text/plain")
"Hello, World!")
#+end_src

Other useful functions which operate on HTTP headers are
=JINGLE:GET-REQUEST-HEADER= and =JINGLE:GET-RESPONSE-HEADER=, which
retrieve the value of the HTTP header associated with the request and
response respectively.

** Status Codes

The =JINGLE:SET-RESPONSE-STATUS= function sets the Status Code for the
HTTP Response.

#+begin_src lisp
(defun foo-handler (params)
(declare (ignore params))
(jingle:set-response-status :accepted)
"Task accepted")
#+end_src

Arguments passed to =JINGLE:SET-RESPONSE-STATUS= may be a number
(e.g. =400=), a keyword (e.g. =:bad-request=), or a string (e.g. =Bad
Request=) of the status code. The following three expressions are
equivalent, and they all set the HTTP Status Code to =400 (Bad
Request)=.

#+begin_src lisp
(jingle:set-response-status 400)
(jingle:set-response-status :bad-request)
(jingle:set-response-status "Bad Request")
#+end_src

Another useful function operating on HTTP Status Codes is
=JINGLE:EXPLAIN-STATUS-CODE=.

#+begin_src lisp
CL-USER> (jingle:explain-status-code 400)
"Bad Request"
CL-USER> (jingle:explain-status-code :bad-request)
"Bad Request"
#+end_src

=JINGLE:STATUS-CODE-KIND= returns the kind of the HTTP Status Code as
classified by [[https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml][IANA]], e.g.

#+begin_src lisp
CL-USER> (jingle:status-code-kind 400)
:CLIENT-ERROR
CL-USER> (jingle:status-code-kind :unauthorized)
:CLIENT-ERROR
CL-USER> (jingle:status-code-kind :internal-server-error)
:SERVER-ERROR
CL-USER> (jingle:status-code-kind :moved-permanently)
:REDIRECTION
CL-USER> (jingle:status-code-kind 100)
:INFORMATIONAL
CL-USER> (jingle:status-code-kind "Accepted")
:SUCCESS
#+end_src

Other HTTP status code predicates you may find useful are
=JINGLE:INFORMATIONAL-CODE-P=, =JINGLE:SUCCESS-CODE-P=,
=JINGLE:REDIRECTION-CODE-P=, =JINGLE:CLIENT-ERROR-CODE-P= and
=JINGLE:SERVER-ERROR-CODE-P=.

** Static Resources

Static resources can be served by adding them using
=JINGLE:STATIC-PATH= method, e.g.

#+begin_src lisp
(jingle:static-path *app* "/static/" "~/public_html/")
#+end_src

You can serve static resources from multiple directories as well. In
order to do that simply install them, before you start up the app.

#+begin_src lisp
(jingle:static-path *app* "/static-1/" "/path/to/static-1/")
(jingle:static-path *app* "/static-2/" "/path/to/static-2/")
(jingle:static-path *app* "/static-3/" "/path/to/static-3/")
#+end_src

** Directory Browser

The =JINGLE:SERVE-DIRECTORY= method installs a middleware which allows
you to browse the contents of a given path. For example the following
code exposes the =~/Documents= and =~/Projects= directories.

#+begin_src lisp
(jingle:serve-directory *app* "/docs" "~/Documents")
(jingle:serve-directory *app* "/projects" "~/Projects")
#+end_src

When accessing the directories from the browser make sure to add a
slash at the end of the paths. For example the above directories will
have to accessed at http://localhost:5000/docs/ and
http://localhost:5000/projects/ respectively, if you are using the
default HTTP port when starting up the app.

** Middlewares

You can use regular [[https://github.com/fukamachi/lack#middlewares][Lack middlewares]] with =jingle= as well. Simply
install them using the =JINGLE:INSTALL-MIDDLEWARE= method.

The following simple middleware pushes a new property to the request
environment, which can be queried by the HTTP handlers.

First, implement the middleware.

#+begin_src lisp
(defun my-middleware (app)
"A custom middleware which pushes a new property to the request
environment and exposes it to HTTP handlers."
(lambda (env)
(setf (getf env :my-middleware/message) "my middleware message")
(funcall app env)))
#+end_src

Then we create a =JINGLE:APP= and install it.

#+begin_src lisp
CL-USER> (defparameter *app* (jingle:make-app))
CL-USER> (jingle:install-middleware *app* #’my-middleware)
#+end_src

An example handler which uses the message placed by our middleware may
look like this.

#+begin_src lisp
(defun my-handler (params)
(declare (ignore params))
(jingle:set-response-status :ok)
(jingle:set-response-header :content-type "text/plain")
(getf jingle:*env* :my-middleware/message))
#+end_src

Finally we have to register our handler and start the app.

#+begin_src lisp
CL-USER> (setf (jingle:route *app* "/my-middleware") #’my-handler)
CL-USER> (jingle:start *app*)
#+end_src

Trying it out using =curl(1)= returns the following response.

#+begin_src shell
$ curl -vvv –get http://localhost:5000/my-middleware
* Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /my-middleware HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 09 Dec 2022 11:42:17 GMT
< Server: Hunchentoot 1.3.0
< Transfer-Encoding: chunked
< Content-Type: text/plain
<
* Connection #0 to host localhost left intact
my middleware message
#+end_src

Here’s an example which uses Lack’s =accesslog= middleware and how to
use it with =jingle=. First, load the respective system, which
provides the middleware, and then simply install it into the =jingle=
app.

#+begin_src lisp
CL-USER> (ql:quickload :lack-middleware-accesslog)
CL-USER> (jingle:install-middleware *app* lack.middleware.accesslog:*lack-middleware-accesslog*)
#+end_src

Search for other middlewares you can already use in Quicklisp, e.g.

#+begin_src lisp
CL-USER> (ql:system-apropos "lack-middleware")
#+end_src

You can use middlewares to push metadata into the environment for HTTP
handlers to use. For example, if your HTTP handlers need to read from
and write to a database, you may want to create a middleware, which
pushes a =CL-DBI= connection into the environment, so that HTTP
handlers can use it, when needed.

In order to clear out all installed middlewares you can use the
=JINGLE:CLEAR-MIDDLEWARES= method, e.g.

#+begin_src lisp
CL-USER> (jingle:clear-middlewares *app*)
#+end_src

** Redirects

Redirects in =jingle= are handled by the =JINGLE:REDIRECT= function.

An example HTTP handler which redirects to [[https://lispcookbook.github.io/cl-cookbook/][The Common Lisp Cookbook]]
looks like this.

#+begin_src lisp
(defun to-the-cookbook (params)
(declare (ignore params))
(jingle:redirect "https://lispcookbook.github.io/cl-cookbook/"))
#+end_src

Register the HTTP handler and start the app.

#+begin_src lisp
CL-USER> (setf (jingle:route *app* "/cookbook") #’to-the-cookbook)
CL-USER> (jingle:start *app*)
#+end_src

Navigate to http://localhost:5000/cookbook and you will be
automatically redirected.

There is also another way for defining redirects using
=JINGLE:REDIRECT-ROUTE=. The following example shows how to install
two redirect routes to your =jingle= app, without having to
explicitely define the HTTP handlers in advance.

#+begin_src lisp
CL-USER> (jingle:redirect-route *app* "/sbcl" "https://sbcl.org/")
CL-USER> (jingle:redirect-route *app* "/ecl" "https://ecl.common-lisp.dev/")
#+end_src

** Request Parameters

The =JINGLE:GET-REQUEST-PARAM= function may be used within HTTP
handlers to get the value associated with a given parameter.

Suppose we have the following example HTTP handler, which returns
information about supported products and is exposed via the
=/api/v1/product/:name= endpoint.

#+begin_src lisp
(defparameter *products*
’((:|id| 1 :|name| "foo")
(:|id| 2 :|name| "bar")
(:|id| 3 :|name| "baz")
(:|id| 4 :|name| "qux")
(:|id| 5 :|name| "foo v2")
(:|id| 6 :|name| "bar v3")
(:|id| 7 :|name| "baz v4")
(:|id| 8 :|name| "qux v5"))
"The list of our supported products")

(defun find-product-by-name (name)
"Finds a product by name"
(find name
*products*
:key (lambda (item) (getf item :|name|))
:test #’string=))

(defun product-handler (params)
"Handles requests for /api/v1/product/:name endpoint"
(jingle:set-response-status :ok)
(jingle:set-response-header :content-type "application/json")
(let* ((name (jingle:get-request-param params :name))
(product (find-product-by-name name)))
(if product
(jonathan:to-json product)
(progn
(jingle:set-response-status :not-found)
(jonathan:to-json ’(:|error| "Product not found"))))))
#+end_src

Register the HTTP handler and start the app.

#+begin_src lisp
CL-USER> (setf (jingle:route *app* "/api/v1/product/:name") #’product-handler)
CL-USER> (jingle:start *app*)
#+end_src

Testing it out with different product names using =curl(1)=.

#+begin_src shell
$ curl -s –get http://localhost:5000/api/v1/product/foo | jq ’.’
{
"id": 1,
"name": "foo"
}

$ curl -s –get http://localhost:5000/api/v1/product/bar | jq ’.’
{
"id": 2,
"name": "bar"
}

$ curl -s –get http://localhost:5000/api/v1/product/unknown | jq ’.’
{
"error": "Product not found"
}
#+end_src

Another example HTTP handler which returns a list of products in a
paginated way, exposed via the =/api/v1/products= endpoint.

#+begin_src lisp
(defun take (items from to)
"A helper function to return the ITEMS between FROM and TO range"
(let* ((len (length items))
(to (if (>= to len) len to)))
(if (>= from len)
nil
(subseq items from to))))

(defun products-handler (params)
"Handles requests for /api/v1/product and returns a page of products"
(jingle:set-response-status :ok)
(jingle:set-response-header :content-type "application/json")
;; Parse the ‘FROM’ and ‘TO’ query parameters. Use default values of
;; 0 and 5 for the params.
(let ((from (parse-integer (jingle:get-request-param params "from" "0") :junk-allowed t))
(to (parse-integer (jingle:get-request-param params "to" "5") :junk-allowed t)))
(cond
((or (null from) (null to)) (jingle:set-response-status :bad-request) nil) ;; NIL added here for the response body
((or (minusp from) (minusp to)) (jingle:set-response-status :bad-request) nil) ;; NIL added here for the response body
(t (jonathan:to-json (take *products* from to))))))
#+end_src

Register the new API endpoint.

#+begin_src lisp
CL-USER> (setf (jingle:route *app* "/api/v1/products") #’products-handler)
#+end_src

Testing it out using =curl(1)= with different values for =from= and
=to= query params.

#+begin_src shell
$ curl -s –get ’http://localhost:5000/api/v1/products?from=0&to=2’ | jq ’.’
[
{
"id": 1,
"name": "foo"
},
{
"id": 2,
"name": "bar"
}
]

$ curl -s –get ’http://localhost:5000/api/v1/products?from=2&to=4’ | jq ’.’
[
{
"id": 3,
"name": "baz"
},
{
"id": 4,
"name": "qux"
}
]
#+end_src

Another way to retrieve request parameter values is to use the
=JINGLE:WITH-REQUEST-PARAMS= macro. The previous example handler can
be rewritten this way.

#+begin_src lisp
(defun products-handler (params)
(jingle:with-json-response
(jingle:with-request-params ((from-param "from" "0") (to-param "to" "5")) params
;; Parse the query parameters and make sure we’ve got good values
(let ((from (parse-integer from-param :junk-allowed t))
(to (parse-integer to-param :junk-allowed t)))
(cond
((or (null from) (null to))
(jingle:set-response-status :bad-request)
nil) ;; NIL added here for the response body
((or (minusp from) (minusp to))
(jingle:set-response-status :bad-request)
nil) ;; NIL added here for the response body
(t (take *products* from to)))))))
#+end_src

** Macros

The following helper macros are available in =jingle=.

- =JINGLE:WITH-JSON-RESPONSE=
- =JINGLE:WITH-REQUEST-PARAMS=
- =JINGLE:WITH-HTML-RESPONSE=

The =JINGLE:WITH-JSON-RESPONSE= macro sets up various HTTP headers
such as =Content-Type= to =application/json= for you and evaluates the
body. The last evaluated expression from the body is encoded as a JSON
object using =JONATHAN:TO-JSON=.

The following example uses =LOCAL-TIME= and =JONATHAN= systems, so
make sure you have them loaded already.

#+begin_src lisp
(defclass ping-response ()
((message
:initarg :message
:initform "pong"
:reader ping-response-message
:documentation "Message to send as part of the response")
(timestamp
:initarg :timestamp
:initform (local-time:now)
:reader ping-response-timestamp))
(:documentation "A response sent as part of a PING request"))

(defmethod jonathan:%to-json ((object ping-response))
(jonathan:with-object
(jonathan:write-key-value "message" (ping-response-message object))
(jonathan:write-key-value "timestamp" (ping-response-timestamp object))))

(defun ping-handler (params)
(declare (ignore params))
(jingle:with-json-response
(make-instance ’ping-response)))
#+end_src

Register the HTTP handler and start the app.

#+begin_src lisp
CL-USER> (setf (jingle:route *app* "/api/v1/ping") #’ping-handler)
CL-USER> (jingle:start *app*)
#+end_src

Trying it you should see results similar to the ones below.

#+begin_src shell
$ curl -s –get http://localhost:5000/api/v1/ping | jq ’.’
{
"message": "pong",
"timestamp": 1670593969
}

$ curl -s –get http://localhost:5000/api/v1/ping | jq ’.’
{
"message": "pong",
"timestamp": 1670593974
}

$ curl -s –get http://localhost:5000/api/v1/ping | jq ’.’
{
"message": "pong",
"timestamp": 1670593976
}
#+end_src

The =JINGLE:WITH-REQUEST-PARAMS= macro provides an easy way to bind
symbols to request params from within HTTP handlers.

#+begin_src lisp
(defun foo-handler (params)
(jingle:with-request-params ((foo "foo") (bar "bar")) params
;; Use FOO and BAR params in order to ...
...))
#+end_src

The =JINGLE:WITH-HTML-RESPONSE= is similar to
=JINGLE:WITH-JSON-RESPONSE=, but sets up the response with a
=Content-Type: text/html; charset=utf-8= header.

** Error Handling

The =JINGLE:BASE-HTTP-ERROR= condition may be used as the base for
user-defined conditions.

If a condition is signalled from within HTTP handlers and the
condition is a sub-class of =JINGLE:BASE-HTTP-ERROR=, then the
=JINGLE:HANDLE-ERROR= method will be invoked.

The purpose of =JINGLE:HANDLE-ERROR= is to handle the error and set up
an appropriate HTTP response, which will be returned to the client.

The rest of this section describes how to create and use custom errors
for a very simple REST API. The API we will develop provides the
following endpoints.

#+begin_src shell
GET /api/v1/product => Returns a list of products (supports ‘from‘ and ‘to‘ query params)
GET /api/v1/product/:name => Returns a product by name, if found
#+end_src

The error responses which we will return to clients would look like this.

#+begin_src javascript
{
"error": "<Reason for the error response>"
}
#+end_src

First we will define our =API-ERROR= condition, and then define the
=JINGLE:HANDLE-ERROR= method on it, so that we return consistent error
responses to our API clients.

#+begin_src lisp
(define-condition api-error (jingle:base-http-error)
()
(:documentation "Represents a condition which will be signalled on API errors"))

(defmethod jingle:handle-error ((error api-error))
"Handles the error and sets up the HTTP error response to be sent to clients"
(with-accessors ((code jingle:http-error-code)
(body jingle:http-error-body)) error
(jingle:set-response-status code)
(jingle:set-response-header :content-type "application/json")
(jonathan:to-json (list :|error| body))))
#+end_src

Next, we will implement some helper functions that signal common
client-error HTTP responses.

#+begin_src lisp
(defun throw-not-found-error (message)
"Throws a 404 (Not Found) HTTP response"
(error ’api-error :code :not-found :body message))

(defun throw-bad-request-error (message)
"Throws a 400 (Bad Request) HTTP response"
(error ’api-error :code :bad-request :body message))
#+end_src

Having our conditions and error-related functions we will also define
another helper function, which will be responsible for parsing HTTP
query parameters as integers, which we will use in our handlers.

#+begin_src lisp
(defun get-int-param (params name &optional default)
"Gets the NAME parameter from PARAMS and parses it as an integer.
In case of invalid input it will signal a 400 (Bad Request) error"
(let ((raw (jingle:get-request-param params name default)))
(typecase raw
(number raw)
(null (throw-bad-request-error (format nil "missing value for ‘~A‘ param" name)))
(string (let ((parsed (parse-integer raw :junk-allowed t)))
(unless parsed
(throw-bad-request-error (format nil "invalid value for ‘~A‘ param" name)))
parsed))
(t (throw-bad-request-error (format nil "unsupported value for ‘~A‘ param" name))))))
#+end_src

We will be building on top of the /products/ API, which was shown in a
previous section. The =*PRODUCTS*= var will be our "database" in this
simple API.

#+begin_src lisp
(defparameter *products*
’((:|id| 1 :|name| "foo")
(:|id| 2 :|name| "bar")
(:|id| 3 :|name| "baz")
(:|id| 4 :|name| "qux")
(:|id| 5 :|name| "foo v2")
(:|id| 6 :|name| "bar v3")
(:|id| 7 :|name| "baz v4")
(:|id| 8 :|name| "qux v5"))
"The list of our supported products")

(defun find-product-by-name (name)
"Finds a product by name"
(find name
*products*
:key (lambda (item) (getf item :|name|))
:test #’string=))

(defun take (items from to)
"A helper function to return the ITEMS between FROM and TO range"
(let* ((len (length items))
(to (if (>= to len) len to)))
(if (>= from len)
nil
(subseq items from to))))
#+end_src

And these are the actual HTTP handlers, which will accept and handle
client requests.

#+begin_src lisp
(defun get-product-handler (params)
"Handles requests for the /api/v1/product/:name endpoint"
(jingle:with-json-response
(let* ((name (jingle:get-request-param params :name))
(product (find-product-by-name name)))
(unless product
(throw-not-found-error "product not found"))
product)))

(defun get-products-page-handler (params)
"Handles requests for the /api/v1/product endpoint"
(jingle:with-json-response
(let ((from (get-int-param params "from" 0))
(to (get-int-param params "to" 2)))
(when (or (minusp from) (minusp to))
(throw-bad-request-error "‘from‘ and ‘to‘ must be positive"))
(take *products* from to))))
#+end_src

Finally, we will create our =JINGLE:APP=, register our handlers and
start serving HTTP requests.

#+begin_src lisp
CL-USER> (defparameter *app* (jingle:make-app))
CL-USER> (setf (jingle:route *app* "/api/v1/product") #’get-products-page-handler)
CL-USER> (setf (jingle:route *app* "/api/v1/product/:name") #’get-product-handler)
CL-USER> (jingle:start *app*)
#+end_src

Time to test things out.

#+begin_src shell
# Getting a page of products using default ‘from‘ and ‘to‘ params
$ curl -s –get ’http://localhost:5000/api/v1/product’ | jq ’.’
[
{
"id": 1,
"name": "foo"
},
{
"id": 2,
"name": "bar"
}
]

# Getting next page of products
$ curl -s –get ’http://localhost:5000/api/v1/product?from=2&to=4’ | jq ’.’
[
{
"id": 3,
"name": "baz"
},
{
"id": 4,
"name": "qux"
}
]

# Passing invalid query params
$ curl -s –get ’http://localhost:5000/api/v1/product?from=bad-value’ | jq ’.’
{
"error": "invalid value for ‘from‘ param"
}

# Passing negative values
$ curl -s –get ’http://localhost:5000/api/v1/product?to=-42’ | jq ’.’
{
"error": "‘from‘ and ‘to‘ must be positive"
}

# Getting a product by name
$ curl -s –get ’http://localhost:5000/api/v1/product/foo’ | jq ’.’
{
"id": 1,
"name": "foo"
}

# Getting a non-existing product
$ curl -s –get ’http://localhost:5000/api/v1/product/unknown’ | jq ’.’
{
"error": "product not found"
}
#+end_src

Great, things work as expected and our API clients will receive
consistent error responses with the proper HTTP status codes set.

** Reverse URLs

When you register routes in your app with names, you can then refer to
these routes by their names. This is useful in situations where you
need to get the URL for a particular route.

In order to get the URL for a route with a particular name use the
=JINGLE:URL-FOR= generic function.

Consider the HTTP handlers we have shown in the previous section of
this document.

#+begin_src lisp
(defun get-product-handler (params)
"Handles requests for the /api/v1/product/:name endpoint"
(jingle:with-json-response
(let* ((name (jingle:get-request-param params :name))
(product (find-product-by-name name)))
(unless product
(throw-not-found-error "product not found"))
product)))

(defun get-products-page-handler (params)
"Handles requests for the /api/v1/product endpoint"
(jingle:with-json-response
(let ((from (get-int-param params "from" 0))
(to (get-int-param params "to" 2)))
(when (or (minusp from) (minusp to))
(throw-bad-request-error "‘from‘ and ‘to‘ must be positive"))
(take *products* from to))))
#+end_src

We can register these handlers and associate them with a name, which we
can later refer to.

#+begin_src lisp
CL-USER> (defparameter *app* (jingle:make-app))
CL-USER> (setf (jingle:route *app* "/api/v1/product" :method :get :identifier "get-products-page") #’get-products-page-handler)
CL-USER> (setf (jingle:route *app* "/api/v1/product/:name" :method :get :identifier "get-product-by-name") #’get-product-handler)
#+end_src

Now, we can get the actual URLs for our HTTP handlers by using their
names.

#+begin_src lisp
CL-USER> (jingle:url-for *app* "get-product-by-name" :name "foo")
#<QURI.URI:URI /api/v1/product/foo>
CL-USER> (jingle:url-for *app* "get-products-page" :|from| 0 :|to| 100)
#<QURI.URI:URI /api/v1/product?from=0&to=100>
#+end_src

Resolving URLs using =JINGLE:URL-FOR= is also useful when you are
creating test cases for your HTTP handlers. Within your test cases
instead of manually constructing the URLs to the respective HTTP
handlers you may refer to them by using their names.

Make sure to also check the =JINGLE.DEMO= system, which uses a
/handler registry/, which is used for registering the HTTP handlers
for a =JINGLE:APP=.

Once you resolve the URL for a particular handler you can construct
the final URL, which will contain scheme, hostname, etc.

#+begin_src lisp
CL-USER> (jingle:url-for *app* "get-product-by-name" :name "foo")
#<QURI.URI:URI /api/v1/product/foo>
CL-USER> (quri:merge-uris * (quri:make-uri :host "example.org" :scheme "https"))
#<QURI.URI.HTTP:URI-HTTPS https://example.org/api/v1/product/foo>
#+end_src

** Testing HTTP Handlers

The =JINGLE:TEST-APP= is a test app meant to be used for test
cases. The difference between =JINGLE:TEST-APP= and =JINGLE:APP= is
that the test app always binds on =127.0.0.1= and listens on a random
port within a given range.

Also, when using =JINGLE:URL-FOR= generic function with a
=JINGLE:TEST-APP= the result is a full URL, which contains the scheme,
the hostname and the port of the running test HTTP server.

Make sure to check the =JINGLE.DEMO.TEST= system for some examples,
which provides the test suite for the demo application.

* Contributing

=jingle= is hosted on [[https://github.com/dnaeon/cl-jingle][Github]]. Please contribute by reporting issues,
suggesting features or by sending patches using pull requests.

* License

This project is Open Source and licensed under the [[http://opensource.org/licenses/BSD-2-Clause][BSD License]].

* Authors

- Marin Atanasov Nikolov <dnaeon@gmail.com>

Version

0.1.0

Dependencies
  • str (system).
  • quri (system).
  • myway (system).
  • lack (system).
  • lack-middleware-static (system).
  • lack-middleware-mount (system).
  • lack-app-directory (system).
  • clack (system).
  • ningle (system).
  • cl-reexport (system).
  • local-time (system).
  • jonathan (system).
  • find-port (system).
Source

jingle.asd.

Child Component

jingle (module).


3 Modules

Modules are listed depth-first from the system components tree.


3.1 jingle/jingle

Source

jingle.asd.

Parent Component

jingle (system).

Child Components

4 Files

Files are sorted by type and then listed depth-first from the systems components trees.


4.1 Lisp


4.1.1 jingle/jingle.asd

Source

jingle.asd.

Parent Component

jingle (system).

ASDF Systems

jingle.

Packages

jingle-system.


4.1.2 jingle/jingle/codes.lisp

Source

jingle.asd.

Parent Component

jingle (module).

Packages

jingle.codes.

Public Interface

4.1.3 jingle/jingle/utils.lisp

Source

jingle.asd.

Parent Component

jingle (module).

Packages

jingle.utils.

Public Interface
Internals

%get-response-header-values (function).


4.1.4 jingle/jingle/core.lisp

Dependencies
Source

jingle.asd.

Parent Component

jingle (module).

Packages

jingle.core.

Public Interface

4.1.5 jingle/jingle/package.lisp

Dependencies
Source

jingle.asd.

Parent Component

jingle (module).

Packages

jingle.


5 Packages

Packages are listed by definition order.


5.1 jingle.codes

Source

codes.lisp.

Use List

common-lisp.

Public Interface

5.2 jingle-system

Source

jingle.asd.

Use List
  • asdf/interface.
  • common-lisp.

5.3 jingle.core

Source

core.lisp.

Use List

common-lisp.

Public Interface

5.4 jingle

Source

package.lisp.

Use List

common-lisp.


5.5 jingle.utils

Source

utils.lisp.

Use List

common-lisp.

Public Interface
Internals

%get-response-header-values (function).


6 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


6.1 Public Interface


6.1.1 Special variables

Special Variable: *env*

*ENV* will be dynamically bound to the Lack environment. It can be used to query the environment from within the HTTP handlers.

Package

jingle.core.

Source

core.lisp.

Special Variable: *status-codes*

HTTP Status Code Registry from IANA

Package

jingle.codes.

Source

codes.lisp.


6.1.2 Macros

Macro: with-html-response (&body body)

A helper macro to be used from within HTTP handlers, which sets the Content-Type to text/html. On error, it will return a response with status code 500 (Internal Server Error) and a short HTML body describing the condition, which was signalled.

Package

jingle.utils.

Source

utils.lisp.

Macro: with-json-response (&body body)

A helper macro to be used from within HTTP handlers, which sets the Content-Type to application/json, and encodes the last evaluated expression of BODY as a JSON object.

Package

jingle.utils.

Source

utils.lisp.

Macro: with-request-params (param-items params-alist &body body)

A helper macro which binds symbols to values from the request parameters, and evalutes BODY.

PARAM-ITEMS is a list of (SYMBOL PARAM-NAME DEFAULT-VALUE) lists, which represent the symbols to be bound while evaluating the BODY.

PARAMS-ALIST is the alist passed to the jingle handler.

Package

jingle.utils.

Source

utils.lisp.


6.1.3 Ordinary functions

Function: client-error-code-p (value)

Returns T, if the HTTP Status code is classified as ‘Client Error’

Package

jingle.codes.

Source

codes.lisp.

Function: explain-status-code (value)

Returns the text notes for the HTTP Status Code by looking up the registry

Package

jingle.codes.

Source

codes.lisp.

Function: get-request-header (name)

Returns two values, first one is a list representing the values of the requested header NAME, and the second is T or NIL, depending on whether the header was set.

Package

jingle.utils.

Source

utils.lisp.

Function: get-request-param (params name &optional default)

Returns the value associated with the NAME param. If NAME param is not set, returns DEFAULT.

Package

jingle.utils.

Source

utils.lisp.

Function: get-response-header (name)

Returns two values, first one is a list representing the values of the requested response header NAME, and the second one is T or NIL, depending on whether the header was set.

Package

jingle.utils.

Source

utils.lisp.

Function: informational-code-p (value)

Returns T, if the HTTP Status code is classified as ‘Informational’

Package

jingle.codes.

Source

codes.lisp.

Function: lookup-status-code (value &key by)

Returns the HTTP Status Code associated with VALUE by looking up the registry

Package

jingle.codes.

Source

codes.lisp.

Function: lookup-status-code-or-lose (value &key by)
Package

jingle.codes.

Source

codes.lisp.

Function: make-app (&key middlewares server-kind address port debug-mode silent-mode use-thread)

Creates a new jingle app

Package

jingle.core.

Source

core.lisp.

Function: make-test-app (&key middlewares server-kind silent-mode port-min port-max)

Creates a new jingle app for testing purposes only. This app is useful only when you are creating test cases for your HTTP handlers, as it binds on 127.0.0.1 and listens on a random port within the PORT-MIN:PORT-MAX range.

Package

jingle.core.

Source

core.lisp.

Function: redirect (location &optional code)

Sets the HTTP status code to CODE and Location header to LOCATION

Package

jingle.utils.

Source

utils.lisp.

Function: redirection-code-p (value)

Returns T, if the HTTP Status code is classified as ‘Redirection’

Package

jingle.codes.

Source

codes.lisp.

Function: request-header-is-set-p (name)

Predicate for testing whether a given request header is set

Package

jingle.utils.

Source

utils.lisp.

Function: response-header-is-set-p (name)

Predicate for testing whether a given response header is set

Package

jingle.utils.

Source

utils.lisp.

Function: server-error-code-p (value)

Returns T, if the HTTP Status code is classified as ‘Server Error’

Package

jingle.codes.

Source

codes.lisp.

Function: set-response-body (body)

Sets the body for the HTTP response to BODY. Internally ningle’s response is an instance of LACK.RESPONSE:RESPONSE. This function is meant to be used from within handlers.

Package

jingle.utils.

Source

utils.lisp.

Function: set-response-header (name value)

Sets the HTTP header with the given NAME to VALUE to be sent to the client as part of the HTTP response. Internally ningle’s response is an instance of LACK.RESPONSE:RESPONSE. This function is meant to be used from within handlers.

Package

jingle.utils.

Source

utils.lisp.

Function: set-response-status (value)

Sets the status code for the HTTP response to CODE. Internally ningle’s response is an instance of LACK.RESPONSE:RESPONSE. This function is meant to be used from within handlers. VALUE may be a number, a keyword or a string representing the HTTP Status Code

Package

jingle.utils.

Source

utils.lisp.

Function: status-code-kind (value)

Returns the kind of the HTTP Status Code

Package

jingle.codes.

Source

codes.lisp.

Function: status-code-number (value)

Returns the HTTP Status Code number associated with VALUE. If VALUE is a number, then return VALUE. If VALUE is a string or keyword then lookup the status code registry for the code.

Package

jingle.codes.

Source

codes.lisp.

Function: success-code-p (value)

Returns T, if the HTTP Status code is classified as ‘Success’

Package

jingle.codes.

Source

codes.lisp.


6.1.4 Generic functions

Generic Reader: address (object)
Generic Writer: (setf address) (object)
Package

jingle.core.

Methods
Reader Method: address ((app app))
Writer Method: (setf address) ((app app))

The address on which the server will listen on

Source

core.lisp.

Target Slot

address.

Generic Function: clear-middlewares (app)

Removes all middlewares from the app

Package

jingle.core.

Source

core.lisp.

Methods
Method: clear-middlewares ((app app))

Removes all installed middlewares from the app

Generic Function: configure (app)

Performs any steps needed to configure the application, before starting it up

Package

jingle.core.

Source

core.lisp.

Methods
Method: configure ((app app))

Sets up the final application by applying all middlewares to our jingle app

Generic Reader: debug-mode (object)
Generic Writer: (setf debug-mode) (object)
Package

jingle.core.

Methods
Reader Method: debug-mode ((app app))
Writer Method: (setf debug-mode) ((app app))

If set to T, will start the app in debug mode

Source

core.lisp.

Target Slot

debug-mode.

Generic Function: find-route (app name)

Finds and returns the route with the given name

Package

jingle.core.

Source

core.lisp.

Methods
Method: find-route ((app app) (name string))

Returns the route with the given NAME, if registered.

Generic Function: handle-error (condition)

Handles the CONDITION by setting up appropriate HTTP response to send to the client

Package

jingle.core.

Source

core.lisp.

Methods
Method: handle-error ((error base-http-error))
Generic Reader: http-error-body (condition)
Generic Writer: (setf http-error-body) (condition)
Package

jingle.core.

Methods
Reader Method: http-error-body ((condition base-http-error))
Writer Method: (setf http-error-body) ((condition base-http-error))
Source

core.lisp.

Target Slot

body.

Generic Reader: http-error-code (condition)
Generic Writer: (setf http-error-code) (condition)
Package

jingle.core.

Methods
Reader Method: http-error-code ((condition base-http-error))
Writer Method: (setf http-error-code) ((condition base-http-error))
Source

core.lisp.

Target Slot

code.

Generic Reader: http-server (object)
Generic Writer: (setf http-server) (object)
Package

jingle.core.

Methods
Reader Method: http-server ((app app))
Writer Method: (setf http-server) ((app app))

The underlying HTTP server of the app. Do not set this slot directly.

Source

core.lisp.

Target Slot

http-server.

Generic Reader: http-server-kind (object)
Generic Writer: (setf http-server-kind) (object)
Package

jingle.core.

Methods
Reader Method: http-server-kind ((app app))
Writer Method: (setf http-server-kind) ((app app))

The HTTP server to use, e.g. :hunchentoot, :woo, etc.

Source

core.lisp.

Target Slot

http-server-kind.

Generic Function: install-middleware (app middleware)

Adds a new middleware to the app

Package

jingle.core.

Source

core.lisp.

Methods
Method: install-middleware ((app app) middleware)

Appends the given MIDDLEWARE to the APP

Generic Reader: middlewares (object)
Generic Writer: (setf middlewares) (object)
Package

jingle.core.

Methods
Reader Method: middlewares ((app app))
Writer Method: (setf middlewares) ((app app))

The list of middlewares for the app

Source

core.lisp.

Target Slot

middlewares.

Generic Reader: port (object)
Generic Writer: (setf port) (object)
Package

jingle.core.

Methods
Reader Method: port ((app app))
Writer Method: (setf port) ((app app))

The port on which the server will listen to

Source

core.lisp.

Target Slot

port.

Generic Function: redirect-route (app path location &key code)

Creates a redirection route for the APP at the given PATH to LOCATION

Package

jingle.core.

Source

core.lisp.

Methods
Method: redirect-route ((app app) path location &key code)
Generic Function: serve-directory (app path root)

Serves the files from the given root directory

Package

jingle.core.

Source

core.lisp.

Methods
Method: serve-directory ((app app) path root)

Mounts the LACK.APP.DIRECTORY:LACK-APP-DIRECTORY app at the given PATH, serving files from ROOT

Generic Reader: silent-mode (object)
Generic Writer: (setf silent-mode) (object)
Package

jingle.core.

Methods
Reader Method: silent-mode ((app app))
Writer Method: (setf silent-mode) ((app app))

Do not output informational messages from Clack, if set to T

Source

core.lisp.

Target Slot

silent-mode.

Generic Function: start (app)

Starts the jingle application and serves requests

Package

jingle.core.

Source

core.lisp.

Methods
Method: start ((app app))

Configures the jingle app and starts serving HTTP requests

Generic Function: static-path (app path root)

Adds a static path to serve files from

Package

jingle.core.

Source

core.lisp.

Methods
Method: static-path ((app app) path root)

Installs the LACK.MIDDLEWARE.STATIC:*LACK-MIDDLEWARE-STATIC* middleware for serving static files at PATH from the given ROOT

Generic Function: stop (app)

Stops the jingle application and the underlying HTTP server

Package

jingle.core.

Source

core.lisp.

Methods
Method: stop ((app app))

Stops the jingle application

Generic Function: url-for (app name &rest params)

Returns the URL for the given route NAME with all PARAMS applied to it

Package

jingle.core.

Source

core.lisp.

Methods
Method: url-for ((app test-app) (name string) &rest params)

Return the URL for the given route NAME with PARAMS applied to it

Method: url-for ((app app) (name string) &rest params)

Return the URL for the given route NAME with PARAMS applied to it

Generic Reader: use-thread (object)
Generic Writer: (setf use-thread) (object)
Package

jingle.core.

Methods
Reader Method: use-thread ((app app))
Writer Method: (setf use-thread) ((app app))

Start server in a separate thread, if set to T

Source

core.lisp.

Target Slot

use-thread.


6.1.5 Standalone methods

Method: call ((app app) env)

Dynamically binds *ENV* to the Lack environment before it hits the HTTP handlers. This way the HTTP handlers can interact with the surrounding environment exposed by Lack by using JINGLE.CORE:*ENV*.

If a condition is signalled and the condition is a sub-class of JINGLE:BASE-HTTP-ERROR, then invoke JINGLE:HANDLE-ERROR for setting up an appropriate HTTP response for the client.

Package

lack.component.

Source

core.lisp.


6.1.6 Conditions

Condition: base-http-error

Base condition class for HTTP errors

Package

jingle.core.

Source

core.lisp.

Direct superclasses

simple-error.

Direct methods
Direct slots
Slot: code

The HTTP Status Code

Initform

(quote (error "must specify the http status code"))

Initargs

:code

Readers

http-error-code.

Writers

(setf http-error-code).

Slot: body

The body to send as part of the HTTP response

Initform

(quote nil)

Initargs

:body

Readers

http-error-body.

Writers

(setf http-error-body).


6.1.7 Classes

Class: app

The jingle app

Package

jingle.core.

Source

core.lisp.

Direct superclasses

app.

Direct subclasses

test-app.

Direct methods
Direct slots
Slot: http-server

The underlying HTTP server of the app. Do not set this slot directly.

Initargs

:http-server

Readers

http-server.

Writers

(setf http-server).

Slot: http-server-kind

The HTTP server to use, e.g. :hunchentoot, :woo, etc.

Initform

:hunchentoot

Initargs

:http-server-kind

Readers

http-server-kind.

Writers

(setf http-server-kind).

Slot: middlewares

The list of middlewares for the app

Initargs

:middlewares

Readers

middlewares.

Writers

(setf middlewares).

Slot: address

The address on which the server will listen on

Initform

"127.0.0.1"

Initargs

:address

Readers

address.

Writers

(setf address).

Slot: port

The port on which the server will listen to

Initform

5000

Initargs

:port

Readers

port.

Writers

(setf port).

Slot: debug-mode

If set to T, will start the app in debug mode

Initform

t

Initargs

:debug-mode

Readers

debug-mode.

Writers

(setf debug-mode).

Slot: silent-mode

Do not output informational messages from Clack, if set to T

Initargs

:silent-mode

Readers

silent-mode.

Writers

(setf silent-mode).

Slot: use-thread

Start server in a separate thread, if set to T

Initform

t

Initargs

:use-thread

Readers

use-thread.

Writers

(setf use-thread).

Class: test-app

A jingle application used for testing purposes only

Package

jingle.core.

Source

core.lisp.

Direct superclasses

app.

Direct methods

url-for.


6.2 Internals


6.2.1 Ordinary functions

Function: %get-response-header-values (name)

Collects all values for the response header with the given NAME

Package

jingle.utils.

Source

utils.lisp.


Appendix A Indexes


A.1 Concepts


A.2 Functions

Jump to:   %   (  
A   C   D   E   F   G   H   I   L   M   P   R   S   U   W  
Index Entry  Section

%
%get-response-header-values: Private ordinary functions

(
(setf address): Public generic functions
(setf address): Public generic functions
(setf debug-mode): Public generic functions
(setf debug-mode): Public generic functions
(setf http-error-body): Public generic functions
(setf http-error-body): Public generic functions
(setf http-error-code): Public generic functions
(setf http-error-code): Public generic functions
(setf http-server): Public generic functions
(setf http-server): Public generic functions
(setf http-server-kind): Public generic functions
(setf http-server-kind): Public generic functions
(setf middlewares): Public generic functions
(setf middlewares): Public generic functions
(setf port): Public generic functions
(setf port): Public generic functions
(setf silent-mode): Public generic functions
(setf silent-mode): Public generic functions
(setf use-thread): Public generic functions
(setf use-thread): Public generic functions

A
address: Public generic functions
address: Public generic functions

C
call: Public standalone methods
clear-middlewares: Public generic functions
clear-middlewares: Public generic functions
client-error-code-p: Public ordinary functions
configure: Public generic functions
configure: Public generic functions

D
debug-mode: Public generic functions
debug-mode: Public generic functions

E
explain-status-code: Public ordinary functions

F
find-route: Public generic functions
find-route: Public generic functions
Function, %get-response-header-values: Private ordinary functions
Function, client-error-code-p: Public ordinary functions
Function, explain-status-code: Public ordinary functions
Function, get-request-header: Public ordinary functions
Function, get-request-param: Public ordinary functions
Function, get-response-header: Public ordinary functions
Function, informational-code-p: Public ordinary functions
Function, lookup-status-code: Public ordinary functions
Function, lookup-status-code-or-lose: Public ordinary functions
Function, make-app: Public ordinary functions
Function, make-test-app: Public ordinary functions
Function, redirect: Public ordinary functions
Function, redirection-code-p: Public ordinary functions
Function, request-header-is-set-p: Public ordinary functions
Function, response-header-is-set-p: Public ordinary functions
Function, server-error-code-p: Public ordinary functions
Function, set-response-body: Public ordinary functions
Function, set-response-header: Public ordinary functions
Function, set-response-status: Public ordinary functions
Function, status-code-kind: Public ordinary functions
Function, status-code-number: Public ordinary functions
Function, success-code-p: Public ordinary functions

G
Generic Function, (setf address): Public generic functions
Generic Function, (setf debug-mode): Public generic functions
Generic Function, (setf http-error-body): Public generic functions
Generic Function, (setf http-error-code): Public generic functions
Generic Function, (setf http-server): Public generic functions
Generic Function, (setf http-server-kind): Public generic functions
Generic Function, (setf middlewares): Public generic functions
Generic Function, (setf port): Public generic functions
Generic Function, (setf silent-mode): Public generic functions
Generic Function, (setf use-thread): Public generic functions
Generic Function, address: Public generic functions
Generic Function, clear-middlewares: Public generic functions
Generic Function, configure: Public generic functions
Generic Function, debug-mode: Public generic functions
Generic Function, find-route: Public generic functions
Generic Function, handle-error: Public generic functions
Generic Function, http-error-body: Public generic functions
Generic Function, http-error-code: Public generic functions
Generic Function, http-server: Public generic functions
Generic Function, http-server-kind: Public generic functions
Generic Function, install-middleware: Public generic functions
Generic Function, middlewares: Public generic functions
Generic Function, port: Public generic functions
Generic Function, redirect-route: Public generic functions
Generic Function, serve-directory: Public generic functions
Generic Function, silent-mode: Public generic functions
Generic Function, start: Public generic functions
Generic Function, static-path: Public generic functions
Generic Function, stop: Public generic functions
Generic Function, url-for: Public generic functions
Generic Function, use-thread: Public generic functions
get-request-header: Public ordinary functions
get-request-param: Public ordinary functions
get-response-header: Public ordinary functions

H
handle-error: Public generic functions
handle-error: Public generic functions
http-error-body: Public generic functions
http-error-body: Public generic functions
http-error-code: Public generic functions
http-error-code: Public generic functions
http-server: Public generic functions
http-server: Public generic functions
http-server-kind: Public generic functions
http-server-kind: Public generic functions

I
informational-code-p: Public ordinary functions
install-middleware: Public generic functions
install-middleware: Public generic functions

L
lookup-status-code: Public ordinary functions
lookup-status-code-or-lose: Public ordinary functions

M
Macro, with-html-response: Public macros
Macro, with-json-response: Public macros
Macro, with-request-params: Public macros
make-app: Public ordinary functions
make-test-app: Public ordinary functions
Method, (setf address): Public generic functions
Method, (setf debug-mode): Public generic functions
Method, (setf http-error-body): Public generic functions
Method, (setf http-error-code): Public generic functions
Method, (setf http-server): Public generic functions
Method, (setf http-server-kind): Public generic functions
Method, (setf middlewares): Public generic functions
Method, (setf port): Public generic functions
Method, (setf silent-mode): Public generic functions
Method, (setf use-thread): Public generic functions
Method, address: Public generic functions
Method, call: Public standalone methods
Method, clear-middlewares: Public generic functions
Method, configure: Public generic functions
Method, debug-mode: Public generic functions
Method, find-route: Public generic functions
Method, handle-error: Public generic functions
Method, http-error-body: Public generic functions
Method, http-error-code: Public generic functions
Method, http-server: Public generic functions
Method, http-server-kind: Public generic functions
Method, install-middleware: Public generic functions
Method, middlewares: Public generic functions
Method, port: Public generic functions
Method, redirect-route: Public generic functions
Method, serve-directory: Public generic functions
Method, silent-mode: Public generic functions
Method, start: Public generic functions
Method, static-path: Public generic functions
Method, stop: Public generic functions
Method, url-for: Public generic functions
Method, url-for: Public generic functions
Method, use-thread: Public generic functions
middlewares: Public generic functions
middlewares: Public generic functions

P
port: Public generic functions
port: Public generic functions

R
redirect: Public ordinary functions
redirect-route: Public generic functions
redirect-route: Public generic functions
redirection-code-p: Public ordinary functions
request-header-is-set-p: Public ordinary functions
response-header-is-set-p: Public ordinary functions

S
serve-directory: Public generic functions
serve-directory: Public generic functions
server-error-code-p: Public ordinary functions
set-response-body: Public ordinary functions
set-response-header: Public ordinary functions
set-response-status: Public ordinary functions
silent-mode: Public generic functions
silent-mode: Public generic functions
start: Public generic functions
start: Public generic functions
static-path: Public generic functions
static-path: Public generic functions
status-code-kind: Public ordinary functions
status-code-number: Public ordinary functions
stop: Public generic functions
stop: Public generic functions
success-code-p: Public ordinary functions

U
url-for: Public generic functions
url-for: Public generic functions
url-for: Public generic functions
use-thread: Public generic functions
use-thread: Public generic functions

W
with-html-response: Public macros
with-json-response: Public macros
with-request-params: Public macros


A.4 Data types