Show me the code!¶
This is a whirlwind tour of WebGear. If you want to learn more, go to the user guide.
Building Request Handlers¶
A request handler is just a function that takes a request value and produces a response value in a monadic context. This is where the business logic of your application lives.
-- The Has (JSONRequestBody Widget) r constraint indicates that this handler
-- can only be called if the request body is a valid JSON that can be parsed
-- to a Widget value.
putWidget :: Has (JSONRequestBody Widget) r => Handler r Widget
putWidget = Kleisli $ \req -> do
let widget = get (Proxy @(JSONRequestBody Widget)) req
saveToDB widget
return (ok200 widget)
Accessing Request Traits¶
Traits are attributes associated with the request. Header values, query parameters, path components, request body are all examples of traits.
The trait values are accessed using the get
function.
someHandler :: Have [ PathVar "widgetId" UUID
, JSONRequestBody Widget
, QueryParam "limit" Integer
] r
=> Handler r a
someHandler = Kleisli $ \req -> do
let
widget = get (Proxy @(JSONRequestBody Widget)) req -- widget :: Widget
wid = get (Proxy @(PathVar "widgetId" UUID)) req -- wid :: UUID
limit = get (Proxy @(QueryParam "limit" Integer)) req -- limit :: Integer
...
return noContent204
Sending Responses¶
The response value returned contains the HTTP status code, response headers, and optionally a body. The body can be any
type that can be converted to a lazy ByteString using the ToByteString
type class.
createWidget = Kleisli $ \req -> do
...
return (created201 widget)
In addition to functions such as ok200
and created201
, there is a generic respond
function that
accepts an HTTP status code, response headers and the body to generate a response.
Routing without TemplateHaskell¶
You can use regular functions instead of TemplateHaskell QuasiQuoters for routing if you prefer that.
-- matches GET /hello/name:String
helloRoute :: Handler '[] String
helloRoute = method @GET
$ path @"hello" $ pathVar @"name" @String $ pathEnd
$ Kleisli $ \req -> do
let name = get (Proxy @(PathVar "name" String)) req
return $ ok200 $ "Hello, " ++ name
Composable routes¶
Compose routes with <|>
. During request routing, each of these routes will be tried sequentially and the first
matching handler will be called.
allRoutes :: Handler r a
allRoutes = createWidget <|> getWidget <|> updateWidget <|> deleteWidget
Since handlers are functions, you can refactor and compose them as you wish. The below example shows an application with v1 and v2 path prefixes.
allRoutes :: Handler r a
allRoutes = v1Routes <|> v2Routes
-- matches any path starting with /v1
v1Routes :: Handler r a
v1Routes = [match| /v1 ]
$ v1CreateWidget <|> v1GetWidget <|> v1UpdateWidget <|> v1DeleteWidget
-- matches any path starting with /v2
v2Routes :: Handler r a
v2Routes = [match| /v2 ]
$ v2CreateWidget <|> v2GetWidget <|> v2UpdateWidget <|> v2DeleteWidget
Middlewares¶
Middlewares are handlers that wrap another handler. They usually modify or enhance the behaviour of the inner
handler. For example, the queryParam
middleware in the example below ensures that the search handler is invoked
only when the request contains a query parameter named limit
.
searchWidget :: Handler '[] a
searchWidget = queryParam @"limit" @Int searchHandler
searchHandler :: Has (QueryParam "limit" Int) r => Handler r a
searchHandler = ...
Typically, you will compose many middlewares to form a route handler. The user guide explains how you can build your own middlewares.
First-class JSON support¶
Use JSON input and output in your handlers without any boilerplate. WebGear uses the
aeson library to handle JSON values. The request body can be read as a JSON
value using jsonRequestBody
middleware. The response can be converted to a lazy ByteString using the jsonResponseBody
middleware.
updateWidget :: Handler '[] ByteString
updateWidget = jsonRequestBody @Widget
$ jsonResponseBody @Widget
$ updateWidgetHandler
updateWidgetHandler :: Has (JSONRequestBody Widget) r => Handler r Widget
updateWidgetHandler = Kleisli $ \req -> do
let widget = get (Proxy @(JSONRequestBody Widget)) req -- widget :: Widget
...
return $ ok200 widget