Skip to content

andrenventer/spin

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

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

About

Write RESTful web apps in Racket.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Racket 98.9%
  • HTML 1.1%