@@ -0,0 +1,141 @@
1+# Using generics
2+
3+```haskell
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE DeriveGeneric #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE TypeOperators #-}
8+module Main (main, api, getLink, routesLinks, cliGet) where
9+
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)
15+
16+import Servant
17+import Servant.Client
18+
19+import Servant.API.Generic
20+import Servant.Client.Generic
21+import Servant.Server.Generic
22+```
23+
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`:
26+
27+```haskell
28+data Routes route = Routes
29+ { _get :: route :- Capture "id" Int :> Get '[JSON] String
30+ , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
31+ }
32+ deriving (Generic)
33+```
34+
35+Then we'll use this data type to define API, links, server and client.
36+
37+## API
38+
39+You can get a `Proxy` of the API using `genericApi`:
40+
41+```haskell
42+api :: Proxy (ToServantApi Routes)
43+api = genericApi (Proxy :: Proxy Routes)
44+```
45+
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`.
48+
49+## Links
50+
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:
54+
55+```haskell
56+getLink :: Int -> Link
57+getLink = fieldLink _get
58+```
59+
60+We can also get all links at once, as a record:
61+
62+```haskell
63+routesLinks :: Routes (AsLink Link)
64+routesLinks = allFieldLinks
65+```
66+
67+## Client
68+
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`.
72+
73+```haskell
74+cliRoutes :: Routes (AsClientT IO)
75+cliRoutes = genericClientHoist
76+ (\x -> runClientM x env >>= either throwIO return)
77+ where
78+ env = error "undefined environment"
79+
80+cliGet :: Int -> IO String
81+cliGet = _get cliRoutes
82+```
83+
84+## Server
85+
86+Finally, probably the most handy usage: we can convert record of handlers into
87+the server implementation:
88+
89+```haskell
90+record :: Routes AsServer
91+record = Routes
92+ { _get = return . show
93+ , _put = return . odd
94+ }
95+
96+app :: Application
97+app = genericServe record
98+
99+main :: IO ()
100+main = do
101+ args <- getArgs
102+ case args of
103+ ("run":_) -> do
104+ putStrLn "Starting cookbook-generic at http://localhost:8000"
105+ run 8000 app
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"
111+```
112+
113+## Using generics together with a custom monad
114+
115+If your app uses a custom monad, here's how you can combine it with
116+generics.
117+
118+```haskell
119+data AppCustomState =
120+ AppCustomState
121+
122+type AppM = ReaderT AppCustomState Handler
123+
124+apiMyMonad :: Proxy (ToServantApi Routes)
125+apiMyMonad = genericApi (Proxy :: Proxy Routes)
126+
127+getRouteMyMonad :: Int -> AppM String
128+getRouteMyMonad = return . show
129+
130+putRouteMyMonad :: Int -> AppM Bool
131+putRouteMyMonad = return . odd
132+
133+recordMyMonad :: Routes (AsServerT AppM)
134+recordMyMonad = Routes {_get = getRouteMyMonad, _put = putRouteMyMonad}
135+
136+-- natural transformation
137+nt :: AppCustomState -> AppM a -> Handler a
138+nt s x = runReaderT x s
139+
140+appMyMonad :: AppCustomState -> Application
141+appMyMonad state = genericServeT (nt state) recordMyMonad