joncfoo

Spock - Hello World

Posted on 2017-11-24

This literate haskell file demonstrates the most basic web application you can write with Spock. A copy of this runnable file is available here. You may run it via stack with the following command:

stack --resolver lts-9.14 runghc --package Spock SpockHello.lhs

Extensions

{-# LANGUAGE OverloadedStrings #-}

Imports

import Web.Spock
    ( SpockM
    , runSpock
    , spock
    , get
    , root
    , text
    )
SpockM type alias to Spock’s route definition monad
runSpock runs a Spock app
spock builds a runnable Spock app given a config and the app definition
get builds a route handler for HTTP GETs
root alias for the root route - i.e. “/”
text sends text as the response body. Content-Type is set to “text/plain”
import Web.Spock.Config
    ( PoolOrConn(PCNoDatabase)
    , defaultSpockCfg
    )
PoolOrConn sum type that we can utilize to let Spock manage database connections
PCNoDatabase value that lets Spock know that we do not intend for it to manage DB connections
defaultSpockCfg function that builds a Spock config with some default values

Data types

data AppSession = EmptySession
data AppState = EmptyState

AppSession represents the data that we wish to persist across a session. e.g. whether a user is signed in, their email address, basic profile info, etc.

AppState represents the data that will be available to all our route handlers in Spock. e.g. configuration parameters set at application startup time - logging level, service locations, etc.

These two data types both have a single empty constructor for now since we will not be making use of Sessions or State.

Web application definition

webapp :: SpockM () AppSession AppState ()
webapp = do
    get root (text "Hello stranger!")

Note the first and last type parameters to SpockM — the first parameter is the type of database pool/connection, which in our case is () since we told Spock that we do not wish for it to manage any such resource. The last parameter is the return type of our web application, which is () since we do not expect our web application to end.

Our webapp consists of a single route: GET / returns “Hello stranger”. Try it out by visiting http://localhost:5000/

Main

main :: IO ()
main = do
    defConfig <- defaultSpockCfg EmptySession PCNoDatabase EmptyState
    runSpock 5000 (spock defConfig webapp)

We build the default Spock configuration by passing 3 values to the function defaultSpockCfg — the initial session value, database pool/connection management value, and initial app state.

Finally, we assemble the web application via spock config webapp and tell Spock to run it on port 5000.

Note: By default, Spock listens on all interfaces.

Exercises

  1. (Easy) Add a route to webapp that handles GET /ohai and responds with the text “O hai!”

  2. (Medium) Add a route to webapp that handles POST /ohai and responds with the text “O hai postie!”

    • You can test this via curl -X POST http://localhost:5000/ohai