@@ -25,12 +25,16 @@ import Servant.Server
2525
2626-- * Example
2727
28−data Greet = Greet { _msg :: Text }
28+-- | A greet message data type
29+newtype Greet = Greet { msg :: Text }
2930 deriving (Generic, Show)
3031
3132instance FromJSON Greet
3233instance ToJSON Greet
3334
35+-- We add some useful annotations to our captures,
36+-- query parameters and request body to make the docs
37+-- really helpful.
3438instance ToCapture (Capture "name" Text) where
3539 toCapture _ = DocCapture "name" "name of the person to greet"
3640
@@ -49,26 +53,41 @@ instance ToSample Greet where
4953
5054-- API specification
5155type TestApi =
56+ -- GET /hello/:name?capital={true, false} returns a Greet as JSON
5257 "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get Greet
58+
59+ -- POST /greet with a Greet as JSON in the request body,
60+ -- returns a Greet as JSON
5361 :<|> "greet" :> ReqBody Greet :> Post Greet
54− :<|> "delete" :> Capture "greetid" Text :> Delete
62+
63+ -- DELETE /greet/:greetid
64+ :<|> "greet" :> Capture "greetid" Text :> Delete
5565
5666testApi :: Proxy TestApi
5767testApi = Proxy
5868
59−-- Server-side handlers
69+-- Server-side handlers.
70+--
71+-- There's one handler per endpoint, which, just like in the type
72+-- that represents the API, are glued together using :<|>.
73+--
74+-- Each handler runs in the 'EitherT (Int, String) IO' monad.
6075server :: Server TestApi
61−server = hello :<|> greet :<|> delete
76+server = helloH :<|> postGreetH :<|> deleteGreetH
6277
63− where hello name Nothing = hello name (Just False)
64− hello name (Just False) = return . Greet $ "Hello, " <> name
65− hello name (Just True) = return . Greet . toUpper $ "Hello, " <> name
78+ where helloH name Nothing = helloH name (Just False)
79+ helloH name (Just False) = return . Greet $ "Hello, " <> name
80+ helloH name (Just True) = return . Greet . toUpper $ "Hello, " <> name
6681
67− greet = return
82+ postGreetH greet = return greet
6883
69− delete _ = return ()
84+ deleteGreetH _ = return ()
7085
71−-- Client-side query functions
86+-- Client-side querying functions
87+--
88+-- They're all derived automatically from the type, and glued together
89+-- with :<|> just like in the type and for the server handlers, except
90+-- that we don't have to implement them!
7291clientApi :: Client TestApi
7392clientApi = client testApi
7493
@@ -77,23 +96,33 @@ postGreet :: Greet -> BaseUrl -> EitherT String IO Greet
7796deleteGreet :: Text -> BaseUrl -> EitherT String IO ()
7897getGreet :<|> postGreet :<|> deleteGreet = clientApi
7998
80−-- Turn the server into a WAI app
99+-- Turn the server into a WAI app. 'serve' is provided by servant,
100+-- more precisely by the Servant.Server module.
81101test :: Application
82102test = serve testApi server
83103
84−-- Documentation
104+-- Generate the data that lets us have API docs. This
105+-- is derived from the type as well as from
106+-- the 'ToCapture', 'ToParam' and 'ToSample' instances from above.
85107docsGreet :: API
86108docsGreet = docs testApi
87109
88−-- Run the server
110+-- Run the server.
111+--
112+-- 'run' comes from Network.Wai.Handler.Warp
89113runTestServer :: Port -> IO ()
90114runTestServer port = run port test
91115
92−-- Run some queries against the server
116+-- Put this all to work!
93117main :: IO ()
94118main = do
119+ -- we start the server, binding it to port 8001
95120 tid <- forkIO $ runTestServer 8001
121+
122+ -- we tell the client where to find it
96123 let uri = BaseUrl Http "localhost" 8001
124+
125+ -- we run a couple of requests against the server
97126 print =<< runEitherT (getGreet "alp" (Just True) uri)
98127 print =<< runEitherT (getGreet "alp" (Just False) uri)
99128 let g = Greet "yo"
@@ -101,4 +130,6 @@ main = do
101130 print =<< runEitherT (deleteGreet "blah" uri)
102131 killThread tid
103132 putStrLn "\n---------\n"
133+
134+ -- we print the markdown docs
104135 putStrLn $ markdown docsGreet