@@ -1,141 +0,0 @@
4−{-# LANGUAGE DataKinds #-}
5−{-# LANGUAGE DeriveGeneric #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE TypeOperators #-}
8−module Main (main, api, getLink, routesLinks, cliGet) where
10−import Control.Exception (throwIO)
11−import Control.Monad.Trans.Reader (ReaderT, runReaderT)
12−import Data.Proxy (Proxy (..))
13−import Network.Wai.Handler.Warp (run)
14−import System.Environment (getArgs)
19−import Servant.API.Generic
20−import Servant.Client.Generic
21−import Servant.Server.Generic
24−The usage is simple, if you only need a collection of routes.
25−First you define a record with field types prefixed by a parameter `route`:
28−data Routes route = Routes
29− { _get :: route :- Capture "id" Int :> Get '[JSON] String
30− , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
35−Then we'll use this data type to define API, links, server and client.
39−You can get a `Proxy` of the API using `genericApi`:
42−api :: Proxy (ToServantApi Routes)
43−api = genericApi (Proxy :: Proxy Routes)
46−It's recommended to use `genericApi` function, as then you'll get
47−better error message, for example if you forget to `derive Generic`.
51−The clear advantage of record-based generics approach, is that
52−we can get safe links very conveniently. We don't need to define endpoint types,
53−as field accessors work as proxies:
56−getLink :: Int -> Link
57−getLink = fieldLink _get
60−We can also get all links at once, as a record:
63−routesLinks :: Routes (AsLink Link)
64−routesLinks = allFieldLinks
69−Even more power starts to show when we generate a record of client functions.
70−Here we use `genericClientHoist` function, which lets us simultaneously
71−hoist the monad, in this case from `ClientM` to `IO`.
74−cliRoutes :: Routes (AsClientT IO)
75−cliRoutes = genericClientHoist
76− (\x -> runClientM x env >>= either throwIO return)
78− env = error "undefined environment"
80−cliGet :: Int -> IO String
81−cliGet = _get cliRoutes
86−Finally, probably the most handy usage: we can convert record of handlers into
87−the server implementation:
90−record :: Routes AsServer
92− { _get = return . show
93− , _put = return . odd
97−app = genericServe record
104− putStrLn "Starting cookbook-generic at http://localhost:8000"
106− -- see this cookbook below for custom-monad explanation
107− ("run-custom-monad":_) -> do
108− putStrLn "Starting cookbook-generic with a custom monad at http://localhost:8000"
109− run 8000 (appMyMonad AppCustomState)
110− _ -> putStrLn "To run, pass 'run' argument: cabal new-run cookbook-generic run"
113−## Using generics together with a custom monad
115−If your app uses a custom monad, here's how you can combine it with
119−data AppCustomState =
122−type AppM = ReaderT AppCustomState Handler
124−apiMyMonad :: Proxy (ToServantApi Routes)
125−apiMyMonad = genericApi (Proxy :: Proxy Routes)
127−getRouteMyMonad :: Int -> AppM String
128−getRouteMyMonad = return . show
130−putRouteMyMonad :: Int -> AppM Bool
131−putRouteMyMonad = return . odd
133−recordMyMonad :: Routes (AsServerT AppM)
134−recordMyMonad = Routes {_get = getRouteMyMonad, _put = putRouteMyMonad}
136−-- natural transformation
137−nt :: AppCustomState -> AppM a -> Handler a
138−nt s x = runReaderT x s
140−appMyMonad :: AppCustomState -> Application
141−appMyMonad state = genericServeT (nt state) recordMyMonad