[racket] Racket Web Server

From: pablo (r at uzy.me)
Date: Sun Dec 11 18:49:31 EST 2011

Hello,

On Sun, Dec 11, 2011 at 11:25 PM, Gerry Weaver <gerryw at compvia.com> wrote:
> Hello All,
>
> I am new to Racket and Lisp/Scheme in general. I am trying to write a simple
> rest web service in Racket, but I'm not making much progress. Unfortunately,
> I don't find the Racket docs to be very helpful. I guess my brain just isn't
> wired right ;-) Asking questions on the mailing list would almost be a step
> by step at this point, so I thought I would spare y'all from that. Anyway,
> I'm trying to find some alternative docs or examples. I had the same problem
> with the Sedna XML stuff, but once I found the scheme docs on Sedna's web
> site I was able to get going. I've been searching the web for some
> docs/examples, but no luck so far. Is there anything out there that would
> help?
>

I had a similar need recently, I developed a little library to use with
Racket's web-server. There's still no documentation, so I didn't put it
on PLaneT yet, but a few explanations might be enough. Don't hesitate to
ask further questions.

You can find the repaus.rkt file here: https://gitorious.org/repaus

In a racket module, you require "repaus.rkt" and then you have three new
form you can use :
- define-resource
- route
- serve

* define-resource allows you to define a… resource! It works like this:

    (define-resource (name path-elements ...)
      (http-method => procedure)
      ...)

- name is the name of the resource.

- path-elements are the request path separated by '/'. They can be
  strings or symbols. Strings enforce the value and symbols are
  "arguments". Symbols in path-elements can be preceded by #:number or
  #:symbol to be converted automatically (to a number or a symbol
  instead of a string). It can ends with ". some-symbol" to get a list
  of all the remaining path-elemnts.

- http-method can be one of get, post, put, delete, patch, head.

- procedure must be a function which takes the request as first
  argument, and then as many arguments as there are symbols in the
  path-elements. Also, it must returns someting the web-server can use
  as a response.

Examples:

    (define-resource (home "")
      ;; /
      (get => (lambda (req) #;"the home page")))

    (define-resource (posts "posts")
      ;; /posts
      (get => (lambda (req) #;"the n lasts posts"))
      (post => (lambda (req) #;"creates a new post")))
      ; see the very good Racket web-server/http docs for how to get
      ; POST data and stuff from the request object.

    (define-resource (post "posts" slug)
      ;; /posts/your-awesome-post, /posts/foo-bar, ...
      (get => (lambda (req slug) #;"the slug post"))
      (put => (lambda (req slug) #;"update the slug post"))
      (delete => (lamdba (req slug) #;"delete the slug post")))

    (define-resource (comments "posts" slug "comments")
      ;; /posts/foo-bar/comments, /posts/lalala/comments, ...
      (get => (lambda (req slug) #;"comments for slug"))
      (post => (lambda (req slug) #;"creates a comment for post slug")))

    (define-resource (comment "posts" slug "comments" #:number id)
      ;; /posts/foo-bar/comments/42
      (put => (lambda (req slug id) #;"edit comment id"))
      (delete => (lambda (req slug id) #;"delete comment id")))
      ; here the id argument of the procedure is a number, I think for
      ; now it just breaks if Racket can't convert it to a number…
      ; (for instance /posts/foo-bar/comments/lala)

    (define-resource (archives "archives" #:number year #:number month)
      ;; now it should be clear)

End examples.

Of course you can (should) define the procedure in other modules that
you require and just do stuff like "(get => show-posts)".

* route will takes some resources defined with define-resource as
arguments and make a functions which does the right dispatching that you
can use with serve:

    (define blog (route home posts post comments comment archives))

* serve is just an alias to serve/servlet from Racket's
web-server/servlet (so you don't need to require it):

    (serve blog
           #:port 8080
           #:listen-ip #f
           #:servlet-regexp #rx""
           #:stateless? #t
           #:command-line? #t)

* Other details:
- OPTIONS request are automatically handled for existing resources.
- an empty response with a 404 (Not Found) status is sent if no
  resources given to route match the request.
- an empty resources with a 405 (Method Not Allowed) status is sent if
  the resource exists but the method is not defined.

Now, be aware that this is not quite polished as I wish it to be before
really giving it to the public: it still needs some documentation, the
default 404 and 405 response should be customizable, and to be a bit
more robust for the string->number conversion.

"I'll do that when I have some time…™"

Cheers :-).

--
Pablo.
http://pablo.rauzy.name/



Posted on the users mailing list.