@@ -0,0 +1,118 @@
1+Note: work in progress.
2+
3+# servant-reflex
4+
5+This library lets you automatically derive [`reflex-dom`](https://github.com/reflex-frp/reflex-dom) clients that query each endpoint of a [`servant`](htps://github.com/haskell-servant/servant) webservice.
6+
7+## Building the example
8+
9+You will need a recent [GHCJS installation](https://github.com/ghcjs/ghcjs), or use the [reflex-platform](https://github.com/reflex-frp/reflex-platform). A [snap 1.0](https://github.com/snapframework) is provided for serving the api. `Snap 1.0` is not yet on Hackage, but we bundle the sources as a git submodule. To install everything with cabal:
10+
11+```
12+git clone https://github.com/mightybyte/servant-reflex
13+cd servant-reflex
14+git submodule update --init --recursive
15+./init-sandbox.sh
16+cd testserver && cabal install --only-dep && cabal build && cd ..
17+cabal install --only-dep
18+cabal build
19+./toSite
20+```
21+
22+The final script copies the ghcjs-generated files into the server's static directory.
23+
24+To install with `reflex-platform`:
25+
26+```
27+git clone https://github.com/mightybyte/servant-reflex
28+cd servant-reflex
29+git submodule update --init --recursive
30+./init-sandbox.sh
31+cd testserver && cabal install --only-dep && cabal build && cd ..
32+path/to/reflex-platform/work-on ghcjs ./.
33+cabal build
34+./toSite.sh
35+```
36+
37+
38+## Example
39+
40+We have a webservice API defined in a module where both the server (compiled with ghc) and the frontend (compiled with ghcjs) can see it:
41+
42+```haskell
43+type API = "getint" :> Get '[JSON] Int
44+ :<|> "sayhi" :> QueryParam "username" String
45+ :> QueryParams "greetings" String
46+ :> QueryFlag "gusto"
47+ :> Get '[JSON] String
48+ :<|> "double" :> ReqBody '[JSON] Double
49+ :> Post '[JSON] Double
50+ :<|> Raw
51+```
52+
53+`servant-reflex` then computes functions that can query the API through an `XhrRequest`.
54+
55+```haskell
56+
57+ runGUI :: forall t m.MonadWidget t m => do
58+
59+ -- servant-reflex computes FRP functions for each API endpoint
60+ let (getint :<|> sayhi :<|> doubleit :<|> _) = client (Proxy :: Proxy API)
61+ (Proxy :: Proxy m)
62+ (constDyn defaultUrl)
63+```
64+
65+These client functions are computed from the API and manage serialization, XhrRequest generation, and deserialization for you. `a` parameters become `Behavior t (Maybe a)` values. You provide a trigger event and receive an `Event t (Maybe r, XhrResponse)`, with responses from the API server (which you would write with `servant-server`).
66+
67+```haskell
68+ -- getint :: MonadWidget t m
69+ -- => Event t () -- ^ Trigger the XHR Request
70+ -- -> m (Event t (Maybe (Int, XhrResponse))) -- ^ Consume the answer
71+
72+ -- sayhi :: MonadWidget t m
73+ -- => Behavior t (Maybe String) -- ^ One input parameter - the 'name'
74+ -- -> Behavior t [String] -- ^ Another input - list of preferred greetings
75+ -- -> Behavior t Bool -- ^ Flag for capitalizing the response
76+ -- -> Event t () -- ^ Trigger the XHR Request
77+ -> m (Event t (Maybe String, XhrResponse))
78+
79+ -- doubleit :: MonadWidget t m
80+ -- => Behavior t (Maybe Double)
81+ -- -> Event t ()
82+ -- -> m (Event t (Maybe Double, XhrResponse))
83+```
84+
85+Plug any of these functions into your reactive frontend to consume backend services without having to build XhrRequests by hand.
86+
87+```haskell
88+ elClass "div" "int-demo" $ do
89+ intButton <- button "Get Int"
90+ serverInts <- fmap fst <$> getint intButton
91+ display =<< holdDyn (Just 0) serverInts
92+
93+ elClass "div" "hello-demo" $ do
94+ nameText <- (current . value) <$> textInput def
95+ greetings <- (fmap words . current . value) <$> textInput def
96+ withGusto <- checkbox def
97+ helloButton <- button "Say hi"
98+ hellos <- fmap fst <$> sayhi nameText greetings withGusto helloButton
99+ display =<< holdDyn Nothing hellos
100+
101+ elClass "div" "demo-double" $ do
102+ inputDouble <- (fmapMaybe readMaybe . current) <$> textInput def
103+ doubleButton <- button "Double it"
104+ outputDouble <- fmap fst <$> doubleit inputDouble doubleButton
105+ display =<< holdDyn Nothing outputDouble
106+```
107+
108+## Input validation
109+
110+The frontend's widgets are sometimes in a state where a valid XHR request can be generated, and sometimes not. When all of the input parameters (`Behavior t (Maybe a)`) are `Just _`, the trigger event will communicate with the server. When any of the inputs is `Nothing`, no XHR request will be made (the event will be silently dropped). In the future input parameters will be encoded as `Behavior t (Either e a)`, and triggers that occur when a Request can't be generated will immediately return a `Left e`, which you could use to draw an error in the page.
111+
112+## Invalid responses
113+
114+For convenience, successful XHR responses are decoded into a `FromHttpAPIData a => Just a`. The `XHRResponse` is also returned, which you can inspect to get more fine-grained information about the response.
115+
116+## TODOs
117+
118+We're still working on thi