(Clerk)

Your One Stop Web Shop
Bob Cratchit holding up Tiny Tim.

Clerk is a web development library targeting the Chez Scheme programming language. It is a robust, decently performant, and powerful tooling library that is intended for the programmer to use as a foundation for their own abstractions. It relies on extremely little, intentionally so, and is designed to remain as small–footprinted as is reasonable for such a multi–faceted system. The library contains the following presently:

  1. C network library, handling socket programming and connection management.
  2. Scheme FastCGI protocol implementation, using the aforementioned C support.
  3. Coroutine–based concurrency system utilizing continuations, supports async and await.
  4. High-level, protocol agnostic FastCGI layer in (clerk desk).
  5. HTTP-specific layer which supports path routing and simplified responses in (clerk web).
  6. Validating HTML 4.01 templating engine, provided by (clerk web paper).
  7. Write-through logging system to emit logs to n locations, in (clerk ledger).

There are also features that are slated to be added, as the need arises in personal use of this library. These features mostly pertain to database connectivity, due to that being the last piece needed for Clerk to be fully usable across a wide range of web development needs. A short list of these possible features are:

  1. Control channel to terminate Clerk systems gracefully.
  2. Automatic handling of HEAD requests.
  3. Support for textual-input-port response content.
  4. Bindings to SQLite3.
  5. Bindings to PostgreSQL.
  6. Simple Database connectivity abstraction, similar to JDBC or ODBC.

Table of Contents

  1. Using Clerk
  2. API Reference
    1. (clerk attention)
    2. (clerk desk)
    3. (clerk web)
    4. (clerk web paper)
    5. (clerk web path)
    6. (clerk web form)
    7. (clerk ledger)
  3. License Notice

Using Clerk

Using Clerk should be as simple as 1-2-3:

  1. Clone the source code from the git repository into your Chez Scheme library directory.
  2. Build the C support library by calling make within the cloned directory.
  3. Within Chez Scheme, simply import (clerk web) and (clerk web paper).

A simple example FastCGI server is as follows:

#!/usr/bin/env scheme-script
(import (except (chezscheme) map div meta)
        (clerk web paper)
        (clerk web))

(web-clerk
 "unix://./example.fcgi"
 (GET "/*" (lambda (r)
  (document "en"
   (head (title "Hello, Clerk!"))
   (body (h1 "Hello, Clerk!"))))))

API Reference

Below are the relevant procedures exported by the different Clerk libraries.

(clerk attention)

(clerk attention) provides a simple green thread/fiber concurrency system utilizing Scheme continuations. All Clerk handlers should operate within an Attention locus in order to run properly. This is done automatically by the (clerk...) and (web-clerk...) procedures documented below.

(yield &optional task)

Yields control back to the runtime, which will then choose another fiber to run. task is a procedure that takes no arguments, which is then added to the evaluation chain. For a more robust way to add tasks to the evaluation chain, see (async...) and (await...).

(async procedure &optional arguments ...)

Yields control back to the runtime, providing a new fiber to evaluation in the form of (procedure arguments...). The fiber calling async is readded to the evaluation chain, and will not wait for the completion of the newly created fiber. Additionally, no communication layer is provided between fibers, so the result of the new fiber should not be relied upon within the calling context.

(await procedure &optional arguments ...)

Yields control back to the runtime, but does not readd the current fiber to the evaluation chain. Instead, it returns a new fiber that will return back to the currently evaluating fiber. This suspends evaluation of the fiber calling await until such a time as when a return value is ready.

(locus procedure &optional arguments ...)

Spawns and evaluates a new evaluation chain, with the first fiber on the chain being of the form (procedure arguments ...). This procedure will block until the evaluation chain is empty.

(clerk desk)

This library exports the high-level FastCGI system. This is protocol-agnostic, and simply provides a request handler with the information and ports provided by the underlying FastCGI layer.

The protopath is a URI-like string that takes the form "scheme://path". Two protopaths are presently supported by Clerk:

  1. "unix://file/path" (Creates a Unix domain socket)
  2. "tcp://ip:port" (Creates a TCP server socket)

A (clerk desk) handler takes the following form:

(lambda (context input output error) ...)

Where the arguments are as follows:

  1. context: The CGI environment provided to Clerk by the upstream.
  2. input: A binary-input-port that reads from the FastCGI standard input stream.
  3. output: A binary-output-port that emits to the FastCGI standard output stream.
  4. error: A binary-output-port that emits to the FastCGI standard error stream.

(parameter context name)

Attempts to collect the value of the environment variable named name, returning #f if it was not found.

(clerk protopath handler)

Creates and evaluates a new Clerk system, bound to the given protopath, and passing all FastCGI requests to the procedure handler. This procedure blocks indefinitely.

(clerk web)

This library creates an HTTP-specific FastCGI layer, built directly on top of (clerk desk). Handlers within this library take on a different form compared to its more agnostic counterpart:

(lambda (request) ... response)

Where the handler receives a web-request object that can be queried with the procedures below, and it is expected to return the relevant response to that request. Responses in (clerk web) can be one of the following types:

  1. web-response: A full HTTP response, built using the syntax respond, as defined below.
  2. html-document: An HTML document as constructed by (clerk web paper)'s document procedure. Content type is assumed to be text/html.
  3. html-tag: An HTML tag as constructed by (clerk web paper)'s document procedure. This allows the return of partial HTML documents, which can then be used by frontend systems like HTMX, if so desired. Content type is assumed to be text/html.
  4. string: A string. Content type is assumed to be text/plain.
  5. bytevector: A bytevector. Content type is assumed to be application/octet-stream.
  6. input-port: An input port, which is then transferred to the output of the request. Content type is assumed to be application/octet-stream.
    Note: Textual ports are not yet supported.

If the response is not a full web-response record, then the content type is inferred, and the status is assumed to be 200.

Additionally, path objects in this library, while seen as strings, are a list of path parts that are compared when handling a request. This process is automatic, but can be used manually through (clerk web path) for further routing within a handler. More information on these paths is documented below.

(request-method request)

Returns a string of the request method. CGI equivalent is REQUEST_METHOD.

(request-path request)

Returns a string of the request path. CGI equivalent is SCRIPT_NAME.

(query-string request)

Returns a list representing the parsed query string. CGI equivalent is QUERY_STRING.

(content-type request)

Returns a string of the request content type. CGI equivalent is CONTENT_TYPE.

(content-length request)

Returns a string of the request content length. CGI equivalent is CONTENT_LENGTH.

(n-content request n)

Returns a bytevector of n-length, that contains part of the request body. This will continue to read beyond the length of content-length, so any handler using this should stop at that length.

(all-content request)

Returns a bytevector of length content-length, which contains the entire unprocessed request body.

(form-urlencoded request)

Returns a form-data list, which is the result of parsing the retrieved request body as a form-urlencoded string. Handlers should check content type before calling this procedure.

(parameter request name)

Returns the value of the CGI variable represented by name, or #f if not found.

(query request name)

Returns the values provided by the query string that matches name, as a list.

(respond ...)

A syntax that takes on multiple forms, dependent upon what is being returned. The forms it supports are as follows:

  1. (respond status body)
  2. (respond status type body)
  3. (respond status (header ...) body)
  4. (respond status type (header ...) body)

Where status is the HTTP response code, as an integer, type is the response content type, as a string, header is a cons pair representing an HTTP header name and value, and finally the body is a valid content body, as defined above.

An example response:

(respond
 200
 "text/plain"
 (("Date" "Tue Jul 14 01:23:13 PM EDT 2026")
  ("X-Custom" "Custom header!"))
 "Hello, world!")

(GET path handler)

Returns a request-handler for the GET method, and the given path.

(POST path handler)

Returns a request-handler for the POST method, and the given path.

(PUT path handler)

Returns a request-handler for the PUT method, and the given path.

(PATCH path handler)

Returns a request-handler for the PATCH method, and the given path.

(DELETE path handler)

Returns a request-handler for the DELETE method, and the given path.

(web-clerk protopath handlers ...)

Blocks, running a web-clerk environment at the given protopath, and with the provided handlers. Handlers are checked from first to last, and as such more general routes should be placed lower in this syntax. Otherwise, a more general route may supercede a more specific one.

(clerk web paper)

This library exports symbols representing every HTML 4.01 tag, as declared in the specification. Additionally, it exports a simple helper procedure as well as a couple duplicate exports due to name collisions. All tag procedures take either of these forms:

  1. (tag &optional body...)
  2. (tag (attribute ...) &optional body ...)

Attribute follows a similar form to response headers, where it simply follows the form of (name value). Both name and value should evaluate to strings.

(document lang head body)

A helper procedure to emit valid HTML 4.01 documents. lang is a string language identifier.

(clerk web path)

This library outputs a simple path comparison system for (clerk web). Paths are a list of path segments, with an optional wildcard that indicates the path should be checked as a prefix. Placing a wildcard in the middle of a path is undefined.

(string->path string)

Convert a string into a path list. For example:

(string->path "/a/b/c") ; => ("a" "b" "c")

(path->string path)

Collapses a path list back into its string representation. Usually just used for logging/display.

(path-matches? matcher matchee)

Returns #t if the matcher matches the matchee, otherwise returns #f. If the matcher path ends with a wildcard ("*"), it checks if it is the prefix of matchee. Otherwise, it only accepts exact matches.

(clerk web form)

This library provides simple utilities for parsing form-urlencoded content types, encoding and decoding strings using URL rules, and querying the resulting parsed values of form-urlencoded.

(url-encode string)

Encodes the provided string to be URL-safe. Uses both percent encoding as well as + in place of space characters.

(url-decode string)

Decode the provided URL-safe string. Supports both percent encoding as well as + in place of space characters.

(parse-form-urlencoded string)

Parses the provided form string into a list with the form:

(("name" . "value") ("name2" . "value2") ...)

Form arrays are supported by adding the name multiple times, each time with a value to be added to the list. This is represented in the returned list by containing multiple name/value pairs for that name, which is then collapsed into a list by query-form

(url-encode-form form-data)

Encodes the given form-data list as a valid, URL-safe string.

(query-form form-data name)

Queries the given form data. This procedure always returns a list of all values associated with the given name. If the name is not found, an empty list is returned.

(clerk ledger)

The ledger logging library, which enables a simple write-through model of logging. It utilizes a list of ledgers that are emitted to, and a simple way to stack a new ledger on top within a scope.

(remark level message &optional arguments ...)

Emits the given message + arguments (ran through format) into the current write stack.

Supported levels are DEBUG, INFO, WARN, and ERROR.

(with-ledger name buckets proc)

Stacks a new ledger, named name, and emitting to the buckets specified by the list buckets, to the write stack. It then calls proc, and will remove the ledger from the stack on termination of the procedure given.

(make-bucket port closable)

Takes in a binary-output-port, and whether or not it is closable, and returns a new bucket procedure. If it is marked as closable, it will close the port if told to close by (clerk ledger).

License Notice

Copyright © 2026 Harald Sorenson

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.