Awesome
Spin
Write RESTful web apps in Racket.
Spin layers some convenience functions on top of Racket's built-in web server to simplify defining routes and route handlers.
Installation
From the command line, run raco pkg install https://github.com/dmac/spin.git
to install the package.
Overview
Define routes with one of get
, post
, put
, patch
, delete
and pass it the route string and a handler function.
#lang racket
(require (planet dmac/spin))
(get "/"
(lambda () "Hello!"))
(run)
Params
Your handler function will be passed the request object if an argument is specified.
It can be given to the params
function along with a key to search for values in the query-string, post-body, or url.
(get "/hi" (lambda (req)
(string-append "Hello, " (params req 'name) "!")))
$ curl "http://localhost:8000/hi?name=Charlotte"
Hello, Charlotte!
$ curl "http://localhost:8000/hi" -X POST -d "name=Anansi"
Hello, Anansi!
Retrieve params from the url string itself:
(get "/hi/:name" (lambda (req)
(string-append "Hello, " (params req 'name) "!")))
$ curl "http://localhost:8000/hi/Peter"
Hello, Peter!
Templating
Your handler function need only return a string to render. You can easily use existing templating libraries with Spin.
app.rkt
(require web-server/templates)
(get "/template" (lambda (req)
(define name (params req 'name))
(include-template "index.html")))
(run)
index.html
<html>
<body>
<p>Hello, @|name|!</p>
</body>
</html>
$ curl "http://localhost:8000/template?name=Aragog"
<html>
<body>
<p>Hello, Aragog!</p>
</body>
</html>
Advanced Responses
In addition to the response body, you can specify response status and custom headers if you return a list instead of a string from your handler:
(get "/headers" (lambda ()
(define h (header #"Custom-Header" #"Itsy bitsy"))
`(201 (,h) "Look for the custom header!")))
Response Makers
Response makers are middleware that transform a response before it is sent to the client.
A global default response maker can be defined by passing it to the run function:
(define (json-404-response-maker status headers body)
(response status
(status->message status)
(current-seconds)
#"application/json; charset=utf-8"
headers
(let ([jsexpr-body (case status
[(404) (string->jsexpr
"{\"error\": 404, \"message\": \"Not Found\"}")]
[else body])])
(lambda (op) (write-json (force jsexpr-body) op)))))
(run #:response-maker json-404-response-maker)
It is also possible to define new handler types that use different response makers:
(define (json-response-maker status headers body)
(response status
(status->message status)
(current-seconds)
#"application/json; charset=utf-8"
headers
(let ([jsexpr-body (string->jsexpr body)])
(lambda (op) (write-json (force jsexpr-body) op)))))
(define (json-get path handler)
(define-handler "GET" path handler json-response-maker))
(json-get "/json" (lambda (req)
"{\"body\":\"JSON GET\"}"))
Contributors
- Felipe Oliveira Carvalho (@philix)
- Jordan Johnson (@RenaissanceBug)