trasa-0.1: Type Safe Web Routing

Safe HaskellNone




Users of this library should a data type representing all possible routes available in a web application. It is recommended that this type be named Route, but this is not required.


Dispatch and Routing

In this example, we will write web application that maintains three counters. The end user will be able to perform various operations that manipulate the values of these counters and ask for their current value. We begin by defining our route type:

>>> :{
data Counter = Red | Green | Blue
  deriving (Show,Read)
data Route :: [Type] -> [Param] -> Bodiedness -> Type -> Type where
  AssignR :: Route '[Counter,Int] '[] 'Bodyless ()
  IncrementR :: Route '[Counter] '[] 'Bodyless Int
  QueryR :: Route '[Counter] '[]Bodyless Int
  TotalR :: Route '[] '[] 'Bodyless Int
data Meta captures querys request response = Meta
  { metaPath :: Path CaptureCodec captures
  , metaQuery :: Rec (Query CaptureCodec) querys
  , metaRequestBody :: RequestBody BodyCodec request
  , metaResponseBody :: ResponseBody BodyCodec response
  , metaMethod :: Text
int :: CaptureCodec Int
int = showReadCaptureCodec
counter :: CaptureCodec Counter
counter = showReadCaptureCodec
bodyUnit :: BodyCodec ()
bodyUnit = BodyCodec (pure "text/plain") (const "") (const (Right ()))
bodyInt :: BodyCodec Int
bodyInt = showReadBodyCodec
meta :: Route captures querys request response -> Meta captures querys request response
meta x = case x of
  AssignR -> Meta
    (match "assign" ./ capture counter ./ match "to" ./ capture int ./ end)
    bodyless (resp bodyUnit) "post"
  IncrementR -> Meta
    (match "increment" ./ capture counter ./ end)
    bodyless (resp bodyInt) "post"
  QueryR -> Meta
    (match "query" ./ capture counter ./ end)
    bodyless (resp bodyInt) "get"
  TotalR -> Meta
    (match "total" ./ end)
    bodyless (resp bodyInt) "get"

Now, we can start using our routes. To do this, we take functions that trasa exports and partially apply them to the route metadata that we have created. We can start with prepare and link:

>>> prepare = prepareWith (metaPath . meta) (metaQuery . meta) (metaRequestBody . meta)
>>> :t prepare
  :: Route captures query request response
     -> Arguments captures query request (Prepared Route response)
>>> :{
link = linkWith (mapPath captureCodecToCaptureEncoding . metaPath . meta)
                (mapQuery captureCodecToCaptureEncoding . metaQuery . meta)
>>> :t link
link :: Prepared Route response -> Url

Now we can use link to encode our routes:

>>> link (prepare AssignR Green 5)