A common question asked about Snap is whether it has support for REST. To quote lambdabot, the superficial answer is “Yes! Snap can do that.” But the truth is that Snap lies at a lower level of abstraction–it is a tool for building any kind of web site, not just REST sites. We would like to announce our new library restful-snap which provides infrastructure for building RESTful web interfaces.

The package is centered around the concept of a Resource. A Resource has a name, root URL, and custom resource- or item-level endpoints. The easiest way of using restful-snap is to use the addResource function to set up your application’s resources. Let’s look at a simple example.

usersResource :: Resource
usersResource = Resource "users" "/admin/users" [] []

usersCrud :: [(CRUD, Handler App App ())]
usersCrud = [ (RNew, newH)
            , (RShow, showH)
            , (REdit, editH)
            , (RUpdate, updateH)
            , (RCreate, createH)
            , (RIndex, indexH)
            ]

Then, in your application initializer you would have something like the following:

appInit = makeSnaplet ... $ do
    h <- nestSnaplet "" heist $ heistInit ""
    -- More app-specific stuff...
    addResource usersResource usersCrud [] []
    return $ App h

This defines a “users” resource located at the URL “/admin/users”. We also have to define Handler functions for whichever CRUD routes we want. Then we pass the resource and the list of CRUD to addResource which sets up all the necessary routes and splices. It adheres pretty closely to the standard established conventions for route schemes.

CRUD URL HTTP Method
RNew /new GET
RShow /:id GET
REdit /:id/edit GET
RUpdate /:id POST
RCreate / POST
RIndex / GET

The addResource function also sets up splices. These splices allow you to embed resource paths into your templates in a checked way, so if you change the location of the resource, your links will still be correct. The above code would generate the following splices (for both compiled and interpreted templates):

  • usersNewPath
  • usersIndexPath
  • usersCreatePath
  • usersPath

Links to item-specific pages are a function of the item ID, so addResource cannot bind those splices by default. For this, the library provides itemSplices and itemCSplices which return the following splices for item URLs as a function of an item ID.

  • usersItemEditPath
  • usersItemShowPath
  • usersItemUpdatePath
  • usersItemDestroyPath

Along with this basic functionality for establishing a routing convention, the restful-snap package also provides infrastructure to facilitate the creation of forms and rendering data structures as HTML. The HasFormlet and PrimSplice type classes let you define default splices and digestive-functors forms for your data types. Having a uniform interface like this allows us to automatically generate basic forms and splices using Template Haskell via the functions deriveHasFormlet, iSplices, and cSplices.