You love scheme
And so do we.
You understand that simplicity is beautiful, and that scheme is the very essence of what it means to write software. But somehow, scheme is perceived as an abstract ideal that can hardly be used in the real world. We begged to differ, so we challenged ourselves to build a credible web application written in our beloved language, and this project was born.
We hope you can find inspiration in it.
Handling HTTP requests
Many have explored the idea of building an HTTP server written in scheme. We instead chose to focus on writing our application logic in scheme, and to rely on the tools offered by Linux for everything else. So we ended up embedding the Chicken Scheme runtime into a FastCGI module hosted by the Apache HTTP server.
The FastCGI foreign interface provides access to the following low level functions:
FCGX_GetParam()
FCGX_GetLineEx()
FCGX_PutS()
The HTTP module then offers the following scheme procedures:
(http-request-method)
(http-read-fastcgi-stream)
(http-write-header)
(http-write-body)
The request processing code is macro generated, so all you have to code looks like this:
(define-http-binding
"GET"
"^customers/(\\d{1,6})$"
get-customer-service
http-parse-get-customer-request
http-format-get-customer-response)
(define (http-parse-get-customer-request route-captures request-body)
(make-get-customer-request
(string->number (car route-captures))))
(define (http-format-get-customer-response response)
(json-format-response response json-format-get-customer-response))
Then all is registered to the Apache HTTP server with this simple configuration:
ScriptAlias /api/ "/usr/local/apache2/api/"
<Directory "/usr/local/apache2/api">
AllowOverride None
Require all granted
SetHandler fcgid-script
Options +ExecCGI
FcgidWrapper /usr/local/apache2/api/scheme virtual
</Directory>
Parsing and formatting JSON
We chose the ubiquitous JSON format to carry our HTTP payloads. The Jansson library fitted our needs nicely, so we built the Jansson foreign interface to provide access to the following low level functions:
json_loads()
json_integer_value()
json_object_set()
json_dumps()
The JSON module then offers the following scheme procedures:
(with-parsed-json-object)
(json-object-property)
(json-object-property-set!)
(json-object->string)
The parsing and formatting code for the JSON requests and responses is macro generated, so all you have to code looks like this:
(define-request new-customer-request
(first-name string #t 1 50)
(last-name string #t 1 50)
(is-vip boolean))
And you automatically gain code capable of parsing HTTP requests such as:
POST /api/customers HTTP/1.1
Host: localhost
{
"first-name": "Alice",
"last-name": "Allison",
"is-vip": true
}
Storing persistent data
We intended to fully exploit our fine-grained control over the HTTP requests, notably through aggressive HTTP caching. So we relaxed our performance requirements at the database level, and ended up leveraging the widely used SQLite library.
The SQLite foreign interface provides access to the following low level functions:
sqlite3_open()
sqlite3_prepare_v2()
sqlite3_bind_int()
sqlite3_step()
The SQL module then offers the following scheme procedures:
(with-sql-connection)
(within-sql-transaction)
(sql-execute)
(sql-read)
The code for the basic CRUD operations is macro generated, so all you have to code looks like this:
(define-table
(customers-table
"customers")
(customer-row
("customer-id" integer)
("first-name" string)
("last-name" string)
("is-vip" boolean))
(custom-selects)
(custom-executes))
And you automatically gain the following procedures:
(make-customer-row)
(customers-table-insert)
(customers-table-select-by-customer-id)
(customers-table-update)
(customers-table-delete)
Spread the scheme love
As you can see, we took great care not impose any design decisions on anyone who would like to build upon this project. Simply knowing you used our foreign interfaces will make us smile. You liked our modules, or even reused some macros? We are happy like it's our birthday.
Please fork this project, set yourself up and build the next big thing in scheme.