Skip to content

First Application

Let us build our first WebGear application. We'll start off with a simple one, the goal of our first application is to respond with the current time for every HTTP request.

Modify src/Main.hs to contain the following code.

import Control.Monad.IO.Class
import Data.Time.Clock
import Network.Wai.Handler.Warp
import WebGear

getTime :: MonadIO m => Kleisli m a (Response String)
getTime = Kleisli $
  \request -> do
    t <- liftIO getCurrentTime
    return $ ok200 $ show t

main :: IO ()
main = run 3000 (toApplication getTime)

Build and run the project:

stack build
stack exec webgear-guide
cabal run webgear-guide

Access http://localhost:3000 and the application should return the current UTC time.

Let us check the code in detail to understand this clearly.

WAI and Warp

We will first examine main function.

Like many other Haskell web frameworks, WebGear uses WAI and Warp. WAI provides an interface for web applications to interact with web servers. Warp is a web server based on WAI.

In the main function, toApplication is used to convert our WebGear handler to a WAI application. This is then passed to run which starts a Warp server serving HTTP requests arriving at port 3000.

Handlers

Now we turn our attention to getTime. This is where the business logic of our application lives. In WebGear terminology this is called a Handler. There are a number of interesting things going on here.

First, this is a function wrapped in Kleisli from Control.Arrow module. These are called Kleisli arrows which is just a fancy term for functions having the type Monad m => a -> m b. So why don't we use just a regular function instead of this Kleisli wrapper? Well, it gives us some useful type class instances such as Alternative and MonadPlus which will come in handy later. We'll see this when we learn about routing.

The function inside Kleisli takes a request value as its parameter and produces a monadic response object as its value. We don't make use of the request in this function because we always return the current time. We also don't care about which monad this runs under as long as it is a MonadIO.

Finally, the response is produced with ok200 $ show t. As you would have guessed, this generates an HTTP 200 OK response with the body produced by show t. We return a String body in this case, but it can be any type with a ToByteString instance.