Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


A really tiny, ready-to-go micro web framework for simple, quick and dirty stuff, based on Ningle. It is ready to go, learning curve almost zero.

What is it good for?

  • When you are coding a bit of Lisp and you need to output some HTML locally on your browser, or JSON, or etc, in the minimal amount of time

  • When you are a beginner and want your Lisp to serve HTML pages or you want to write a minimal Lisp backend.


Not as small as Ningle and not as big as Caveman2.
Ningle was too minimal, so Ninglex adds just a few functions and macros on top of Ningle, then gets you ready to go!

Ninglex is only about defining your routes and your route handlers. Starting and stopping the server. The rest is left to your control.

Underlying Ninglex is Eitaro Fukamachi's Clack & Lack, which allows different servers, so your app can be hosted using Hunchentoot Wookie, etc. What this means is that there's something that you want to do with Ninglex and you don't know how to do, you can do it by glancing at Clack and Lack's documentation.


See example directory and load system "ninglex-example". Don't have time for that? This is most of example.lisp, assuming you have loaded libraries "jonathan" and "spinneret", otherwise example 2 and 3 below will not work:

For newbies: Make sure you load the package. For this example we'll be inside the package "Ninglex". If we are on other package we'll have to prefix all function calls with "ninglex:"

;; load lib
(ql:quickload :ninglex)
(in-package :ninglex)

Example 1: Define your own route handler function, which will take two params: "age" and "name".

This will make your server answer GET requests on http://localhost:5000

The supplied parameters "name" and "age" will be available as "n" and "a" values.

(defun my-fun (params)
  (with-request-params params ((n "name") (a "age"))
     (format nil "Hello, ~a of ~a years old!" n a))))

;; and then, I bind this function to a route 
(set-route "/hello" #'my-fun)

Now we need to start the server

(start ) 

Try: http://localhost:5000/hello?name=XYZ&age=99

(stop) stops the server.

We can do the same, but in less lines, without having to do a "defun".

(with-route ("/hello2" my-params)
  (with-request-params my-params ((n "name") (a "age"))
       (format nil "Hello, ~a of ~a years old!" n a))))

Want to capture parametrized URLs? This example is useful: (here we are using spinneret to output html)

(with-route ("/person/:name" params)
  (with-request-params params ((n :name))
     (with-output-to-string (*html*)
         (:p :class "title is-1" n)

;; etc

Want to output JSON? make sure you load the Jonathan library (Newbies: do (ql:quickload "jonathan"))

;; FYI: "jojo" is synonymous for the Jonathan package
(with-route ("/jsontest" params) 
  (declare (ignore params))
  (json-response ;like string-response but sets correct http content-type
   (jojo:to-json '(:|name| "Common Lisp" :born 1984 :impls (SBCL CLISP)))))

Try: http://localhost:5000/jsontest

Want to output HTML? Ok, let's use the "spinneret" library by Ruricolist (of course you can use other HTML library):

(with-route ("/html-hello" params)
  (declare (ignore params))
  (html-response  ;this just sets the content-type accordingly
   (with-output-to-string (*html*)
         (:title "title"))
        (:body (:h1 "Hello Common Lisp!")
               (:img :src "static/logo-compact.png")))))))

The above example uses a static file dir thus needs the following:

;; Set static root directory for serving the static files
(defparameter *static-root*
  (merge-pathnames #P"static/"
                    (or *load-pathname*

(defun start-example ()
  "Start the server"
  (start :static-root *static-root*))

We can (stop ) the server and start again:

(start-example )

The above example showed 90% of what you need about Ninglex. Want to define a handler for POST requests? use :POST instead of :GET on set-route or with-route

More info available by taking a look at Ningle.


Ninglex is Ningle eXtended, that is, it is based on Eitaro Fukamachi's ningle. As well as based of course in Eitaro's Clack and Lack. Thanks Eitaro!


Licensed under the MIT license.


Easy to learn, quick and dirty, bare-bones web framework for Common Lisp








No releases published


No packages published