fnhaskell.com/tutorial
data Ctxt = Ctxt FnRequest
instance RequestContext Ctxt where
getRequest (Ctxt req) = req
setRequest (Ctxt oldReq) newReq = Ctxt newReqWeb Application Interface
Common interface for frameworks and libraries
Lots of nice middleware for logging, sessions, etc
the actual server
import Web.Fn
import Network.Wai (Application)
import Network.Wai.Handler.Warp (run)
main :: IO ()
main = run 3000 waiApp
waiApp :: Application
waiApp = toWAI (Ctxt defaultFnRequest) siterun :: Int → Application → IO ()
toWAI :: ctxt → (ctxt → IO Response) → Application
site :: ctxt → IO Response
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH ]
`fallthrough` notFoundText "Page not found."
indexH :: Ctxt -> IO (Maybe Response)
indexH ctxt = okText "Welcome to my first Haskell website."
import Web.Fn
import Network.Wai (Application)
import Network.Wai.Handler.Warp (run)
data Ctxt = Ctxt FnRequest
instance RequestContext Ctxt where
getRequest (Ctxt req) = req
setRequest (Ctxt oldReq) newReq = Ctxt newReq
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH ]
`fallthrough` notFoundText "Page not found."
indexH :: Ctxt -> IO (Maybe Response)
indexH ctxt = okText "Welcome to my first Haskell website."
main :: IO ()
main = run 3000 waiApp
waiApp :: Application
waiApp = toWAI (Ctxt defaultFnRequest) site
routes :: Ctxt -> IO (Maybe Response)
routes ctxt =
route ctxt [ {- maybe matches ==> maybe handles -}
, {- maybe matches ==> maybe handles -}
, {- maybe matches ==> maybe handles -} ]
site :: Ctxt -> IO Response
site = routes ctxt `fallthrough` notFoundText "not found"maybe a route matches, maybe a handler returns a response
if not: fallthrough to this other response
end path "blah" method "GET" anything
matches when there's nothing left
matches "blah"
matches only GET requests
matches anything!
segment param "blah"
passes a segment to the handler
passes the value of param "blah" to the the handler
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // segment // end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")helloNameH handles "/hello/workjelly"
"Hello, workjelly!"
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // segment // end ==> helloNameH
, path "hello" ==> rudeHelloH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name =
if name == "dbp"
then return Nothing
else okText ("Hello, " <> name <> "!")
rudeHelloH :: Ctxt -> Text -> IO (Maybe Response)
rudeHelloH ctxt name = okText "Not you again."
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // param "name" //
end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")helloNameH handles "/hello?name=workjelly"
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // segment // end ==> helloNameH
, path "hello" // param "name" //
end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")helloNameH handles "/hello/workjelly" and "/hello?name=workjelly"
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "add" // segment
// segment
// end ==> addNumbersH
, path "add" // segment
// segment
// end ==> addWordsH ]
`fallthrough` notFoundText "Page not found."Same pattern, but two different handlers
localhost:6000/add/(segment)/(segment)
addNumbersH :: Ctxt -> Int -> Int -> IO (Maybe Response)
addNumbersH ctxt number1 number2 =
let sum = number1 + number2 in
okText (show number1 <> " plus " <>
show number2 <> " is " <> sum <> ".")
addWordsH :: Ctxt -> Text -> Text -> IO (Maybe Response)
addWordsH ctxt word1 word2 =
okText (word1 <> " plus " <> word2 <>
" is " <> word1 <> word2 <> ".")
localhost:6000/add/1/2
1 plus 2 is 3
localhost:6000/add/work/jelly
work plus jelly is workjelly
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "add" // segment // segment // end ==> addNumbersH
, path "add" // segment // segment // end ==> addWordsH ]
`fallthrough` notFoundText "Page not found."
addNumbersH :: Ctxt -> Int -> Int -> IO (Maybe Response)
addNumbersH ctxt number1 number2 =
let sum = number1 + number2 in
okText (show number1 <> " plus " <>
show number2 <> " is " <> sum <> ".")
addWordsH :: Ctxt -> Text -> Text -> IO (Maybe Response)
addWordsH ctxt word1 word2 =
okText (word1 <> " plus " <> word2 <> " is " <> word1 <> word2 <> ".")