@@ -0,0 +1,576 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE InstanceSigs #-}
6+{-# LANGUAGE OverloadedStrings #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE TypeOperators #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+#if !MIN_VERSION_base(4,8,0)
12+{-# LANGUAGE OverlappingInstances #-}
13+#endif
14+-- | This module provides 'client' which can automatically generate
15+-- querying functions for each endpoint just from the type representing your
16+-- API.
17+module Servant.Reflex
18+ ( client
19+ , HasReflexClient(..)
20+ , ServantError(..)
21+ , module Servant.Common.BaseUrl
22+ ) where
23+
24+#if !MIN_VERSION_base(4,8,0)
25+import Control.Applicative ((<$>))
26+#endif
27+import Control.Monad
28+import Control.Monad.Trans.Except
29+import Data.ByteString.Lazy (ByteString)
30+import Data.List
31+import Data.Proxy
32+import Data.String.Conversions
33+import Data.Text (unpack)
34+import GHC.TypeLits
35+import Network.HTTP.Media
36+import qualified Network.HTTP.Types as H
37+import qualified Network.HTTP.Types.Header as HTTP
38+import Servant.API
39+import Servant.Common.BaseUrl
40+import Servant.Common.Req
41+import Web.HttpApiData
42+
43+-- * Accessing APIs as a Client
44+
45+-- | 'client' allows you to produce operations to query an API from a client.
46+--
47+-- > type MyApi = "books" :> Get '[JSON] [Book] -- GET /books
48+-- > :<|> "books" :> ReqBody '[JSON] Book :> Post Book -- POST /books
49+-- >
50+-- > myApi :: Proxy MyApi
51+-- > myApi = Proxy
52+-- >
53+-- > getAllBooks :: ExceptT String IO [Book]
54+-- > postNewBook :: Book -> ExceptT String IO Book
55+-- > (getAllBooks :<|> postNewBook) = client myApi host
56+-- > where host = BaseUrl Http "localhost" 8080
57+client :: HasReflexClient layout => Proxy layout -> BaseUrl -> Input layout -> Client layout
58+client p baseurl = clientWithRoute p defReq baseurl
59+
60+type Final a = forall m. m a
61+
62+data a ::> b = a ::> b deriving (Eq,Ord,Show,Read)
63+
64+infixr 3 ::>
65+
66+-- | This class lets us define how each API combinator
67+-- influences the creation of an HTTP request. It's mostly
68+-- an internal class, you can just use 'client'.
69+class HasReflexClient layout where
70+ type Input layout :: *
71+ type Client layout :: *
72+ clientWithRoute :: Proxy layout -> Req -> BaseUrl -> Input layout -> Client layout
73+
74+
75+-- | If you have a 'Get' endpoint in your API, the client
76+-- side querying function that is created when calling 'client'
77+-- will just require an argument that specifies the scheme, host
78+-- and port to send the request to.
79+instance
80+#if MIN_VERSION_base(4,8,0)
81+ {-# OVERLAPPABLE #-}
82+#endif
83+ (MimeUnrender ct result) => HasReflexClient (Get (ct ': cts) result) where
84+ type Input (Get (ct ': cts) result) = ()
85+ type Client (Get (ct ': cts) result) = Final result
86+ clientWithRoute Proxy req baseurl _ =
87+ snd <$> performRequestCT (Proxy :: Proxy ct) H.methodGet req baseurl
88+
89+instance
90+#if MIN_VERSION_base(4,8,0)
91+ {-# OVERLAPPING #-}
92+#endif
93+ HasReflexClient (Get (ct ': cts) ()) where
94+ type Input (Get (ct ': cts) ()) = ()
95+ type Client (Get (ct ': cts) ()) = Final ()
96+ clientWithRoute Proxy req baseurl _ =
97+ performRequestNoBody H.methodGet req baseurl
98+
99+-- | Pick a 'Method' and specify where the server you want to query is. You get
100+-- back the full `Response`.
101+instance HasReflexClient Raw where
102+ type Input Raw = H.Method ::> ()
103+ type Client Raw = Final (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
104+
105+ clientWithRoute :: Proxy Raw -> Req -> BaseUrl -> Input Raw -> Client Raw
106+ clientWithRoute Proxy req baseurl (httpMethod ::> ()) = do
107+ performRequest httpMethod req baseurl
108+
109+-- -- | If you have a 'Get xs (Headers ls x)' endpoint, the client expects the
110+-- -- corresponding headers.
111+-- instance
112+-- #if MIN_VERSION_base(4,8,0)
113+-- {-# OVERLAPPING #-}
114+-- #endif
115+-- ( MimeUnrender ct a, BuildHeadersTo ls
116+-- ) => HasReflexClient (Get (ct ': cts) (Headers ls a)) where
117+-- type Input (Get (ct ': cts) (Headers ls a)) = ()
118+-- type Client (Get (ct ': cts) (Headers ls a)) = Final (Headers ls a)
119+-- clientWithRoute Proxy req baseurl _ = do
120+-- (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodGet req baseurl
121+-- return $ Headers { getResponse = resp
122+-- , getHeadersHList = buildHeadersTo hdrs
123+-- }
124+--
125+-- -- | A client querying function for @a ':<|>' b@ will actually hand you
126+-- -- one function for querying @a@ and another one for querying @b@,
127+-- -- stitching them together with ':<|>', which really is just like a pair.
128+-- --
129+-- -- > type MyApi = "books" :> Get '[JSON] [Book] -- GET /books
130+-- -- > :<|> "books" :> ReqBody '[JSON] Book :> Post Book -- POST /books
131+-- -- >
132+-- -- > myApi :: Proxy MyApi
133+-- -- > myApi = Proxy
134+-- -- >
135+-- -- > getAllBooks :: ExceptT String IO [Book]
136+-- -- > postNewBook :: Book -> ExceptT String IO Book
137+-- -- > (getAllBooks :<|> postNewBook) = client myApi host
138+-- -- > where host = BaseUrl Http "localhost" 8080
139+-- instance (HasReflexClient a, HasReflexClient b) => HasReflexClient (a :<|> b) where
140+-- type Input (a :<|> b) = Input a :<|> Input b
141+-- type Client (a :<|> b) = Client a :<|> Client b
142+-- clientWithRoute Proxy req baseurl (a :<|> b) =
143+-- clientWithRoute (Proxy :: Proxy a) req baseurl a :<|>
144+-- clientWithRoute (Proxy :: Proxy b) req baseurl b
145+--
146+-- ------------------------------------------------------------------------------
147+-- -- | If you use a 'Capture' in one of your endpoints in your API,
148+-- -- the corresponding querying function will automatically take
149+-- -- an additional argument of the type specified by your 'Capture'.
150+-- -- That function will take care of inserting a textual representation
151+-- -- of this value at the right place in the request path.
152+-- --
153+-- -- You can control how values for this type are turned into
154+-- -- text by specifying a 'ToHttpApiData' instance for your type.
155+-- --
156+-- -- Example:
157+-- --
158+-- -- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
159+-- -- >
160+-- -- > myApi :: Proxy MyApi
161+-- -- > myApi = Proxy
162+-- -- >
163+-- -- > getBook :: Text -> ExceptT String IO Book
164+-- -- > getBook = client myApi host
165+-- -- > where host = BaseUrl Http "localhost" 8080
166+-- -- > -- then you can just use "getBook" to query that endpoint
167+-- instance (KnownSymbol capture, ToHttpApiData a, HasReflexClient sublayout)
168+-- => HasReflexClient (Capture capture a :> sublayout) where
169+--
170+-- type Input (Capture cap a :> sublayout) = a ::> Input sublayout
171+-- type Client (Capture capture a :> sublayout) = Client sublayout
172+--
173+-- clientWithRoute Proxy req baseurl (val ::> rest) =
174+-- clientWithRoute (Proxy :: Proxy sublayout)
175+-- (appendToPath p req) baseurl rest
176+--
177+-- where p = unpack (toUrlPiece val)
178+--
179+-- -- | If you have a 'Delete' endpoint in your API, the client
180+-- -- side querying function that is created when calling 'client'
181+-- -- will just require an argument that specifies the scheme, host
182+-- -- and port to send the request to.
183+-- instance
184+-- #if MIN_VERSION_base(4,8,0)
185+-- {-# OVERLAPPABLE #-}
186+-- #endif
187+-- (MimeUnrender ct a, cts' ~ (ct ': cts)) => HasReflexClient (Delete cts' a) where
188+-- type Input (Delete cts' a) = ()
189+-- type Client (Delete cts' a) = Final a
190+-- clientWithRoute Proxy req baseurl val =
191+-- snd <$> performRequestCT (Proxy :: Proxy ct) H.methodDelete req baseurl val
192+--
193+-- instance
194+-- #if MIN_VERSION_base(4,8,0)
195+-- {-# OVERLAPPING #-}
196+-- #endif
197+-- HasReflexClient (Delete cts ()) where
198+-- type Input (Delete cts ()) = ()
199+-- type Client (Delete cts ()) = Final ()
200+-- clientWithRoute Proxy req baseurl val =
201+-- void $ performRequestNoBody H.methodDelete req baseurl val
202+--
203+-- -- | If you have a 'Delete xs (Headers ls x)' endpoint, the client expects the
204+-- -- corresponding headers.
205+-- instance
206+-- #if MIN_VERSION_base(4,8,0)
207+-- {-# OVERLAPPING #-}
208+-- #endif
209+-- ( MimeUnrender ct a, BuildHeadersTo ls, cts' ~ (ct ': cts)
210+-- ) => HasReflexClient (Delete cts' (Headers ls a)) where
211+-- type Input (Delete cts' (Headers ls a)) = ()
212+-- type Client (Delete cts' (Headers ls a)) = Final (Headers ls a)
213+-- clientWithRoute Proxy req baseurl _ = do
214+-- (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodDelete req baseurl
215+-- return $ Headers { getResponse = resp
216+-- , getHeadersHList = buildHeadersTo hdrs
217+-- }
218+--
219+-- -- | If you use a 'Header' in one of your endpoints in your API,
220+-- -- the corresponding querying function will automatically take
221+-- -- an additional argument of the type specified by your 'Header',
222+-- -- wrapped in Maybe.
223+-- --
224+-- -- That function will take care of encoding this argument as Text
225+-- -- in the request headers.
226+-- --
227+-- -- All you need is for your type to have a 'ToHttpApiData' instance.
228+-- --
229+-- -- Example:
230+-- --
231+-- -- > newtype Referer = Referer { referrer :: Text }
232+-- -- > deriving (Eq, Show, Generic, FromText, ToHttpApiData)
233+-- -- >
234+-- -- > -- GET /view-my-referer
235+-- -- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
236+-- -- >
237+-- -- > myApi :: Proxy MyApi
238+-- -- > myApi = Proxy
239+-- -- >
240+-- -- > viewReferer :: Maybe Referer -> ExceptT String IO Book
241+-- -- > viewReferer = client myApi host
242+-- -- > where host = BaseUrl Http "localhost" 8080
243+-- -- > -- then you can just use "viewRefer" to query that endpoint
244+-- -- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
245+-- instance (KnownSymbol sym, ToHttpApiData a, HasReflexClient sublayout)
246+-- => HasReflexClient (Header sym a :> sublayout) where
247+--
248+-- type Input (Header sym a :> sublayout) = Maybe a ::> Input sublayout
249+-- type Client (Header sym a :> sublayout) = Client sublayout
250+--
251+-- clientWithRoute Proxy req baseurl (mval ::> rest) =
252+-- clientWithRoute (Proxy :: Proxy sublayout)
253+-- (maybe req
254+-- (\value -> Servant.Common.Req.addHeader hname value req)
255+-- mval
256+-- )
257+-- baseurl rest
258+--
259+-- where hname = symbolVal (Proxy :: Proxy sym)
260+--
261+-- -- | If you have a 'Post' endpoint in your API, the client
262+-- -- side querying function that is created when calling 'client'
263+-- -- will just require an argument that specifies the scheme, host
264+-- -- and port to send the request to.
265+-- instance
266+-- #if MIN_VERSION_base(4,8,0)
267+-- {-# OVERLAPPABLE #-}
268+-- #endif
269+-- (MimeUnrender ct a) => HasReflexClient (Post (ct ': cts) a) where
270+-- type Input (Post (ct ': cts) a) = ()
271+-- type Client (Post (ct ': cts) a) = Final a
272+-- clientWithRoute Proxy req baseurl _ =
273+-- snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPost req baseurl
274+--
275+-- instance
276+-- #if MIN_VERSION_base(4,8,0)
277+-- {-# OVERLAPPING #-}
278+-- #endif
279+-- HasReflexClient (Post (ct ': cts) ()) where
280+-- type Input (Post (ct ': cts) ()) = ()
281+-- type Client (Post (ct ': cts) ()) = Final ()
282+-- clientWithRoute Proxy req baseurl _ =
283+-- void $ performRequestNoBody H.methodPost req baseurl
284+--
285+-- -- | If you have a 'Post xs (Headers ls x)' endpoint, the client expects the
286+-- -- corresponding headers.
287+-- instance
288+-- #if MIN_VERSION_base(4,8,0)
289+-- {-# OVERLAPPING #-}
290+-- #endif
291+-- ( MimeUnrender ct a, BuildHeadersTo ls
292+-- ) => HasReflexClient (Post (ct ': cts) (Headers ls a)) where
293+-- type Input (Post (ct ': cts) (Headers ls a)) = ()
294+-- type Client (Post (ct ': cts) (Headers ls a)) = Final (Headers ls a)
295+-- clientWithRoute Proxy req baseurl _ = do
296+-- (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPost req baseurl
297+-- return $ Headers { getResponse = resp
298+-- , getHeadersHList = buildHeadersTo hdrs
299+-- }
300+--
301+-- -- | If you have a 'Put' endpoint in your API, the client
302+-- -- side querying function that is created when calling 'client'
303+-- -- will just require an argument that specifies the scheme, host
304+-- -- and port to send the request to.
305+-- instance
306+-- #if MIN_VERSION_base(4,8,0)
307+-- {-# OVERLAPPABLE #-}
308+-- #endif
309+-- (MimeUnrender ct a) => HasReflexClient (Put (ct ': cts) a) where
310+-- type Input (Put (ct ': cts) a) = ()
311+-- type Client (Put (ct ': cts) a) = Final a
312+-- clientWithRoute Proxy req baseurl _ =
313+-- snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPut req baseurl
314+--
315+-- instance
316+-- #if MIN_VERSION_base(4,8,0)
317+-- {-# OVERLAPPING #-}
318+-- #endif
319+-- HasReflexClient (Put (ct ': cts) ()) where
320+-- type Input (Put (ct ': cts) ()) = ()
321+-- type Client (Put (ct ': cts) ()) = Final ()
322+-- clientWithRoute Proxy req baseurl _ =
323+-- void $ performRequestNoBody H.methodPut req baseurl
324+--
325+-- -- | If you have a 'Put xs (Headers ls x)' endpoint, the client expects the
326+-- -- corresponding headers.
327+-- instance
328+-- #if MIN_VERSION_base(4,8,0)
329+-- {-# OVERLAPPING #-}
330+-- #endif
331+-- ( MimeUnrender ct a, BuildHeadersTo ls
332+-- ) => HasReflexClient (Put (ct ': cts) (Headers ls a)) where
333+-- type Input (Put (ct ': cts) (Headers ls a)) = ()
334+-- type Client (Put (ct ': cts) (Headers ls a)) = Final (Headers ls a)
335+-- clientWithRoute Proxy req baseurl _ = do
336+-- (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPut req baseurl
337+-- return $ Headers { getResponse = resp
338+-- , getHeadersHList = buildHeadersTo hdrs
339+-- }
340+--
341+-- -- | If you have a 'Patch' endpoint in your API, the client
342+-- -- side querying function that is created when calling 'client'
343+-- -- will just require an argument that specifies the scheme, host
344+-- -- and port to send the request to.
345+-- instance
346+-- #if MIN_VERSION_base(4,8,0)
347+-- {-# OVERLAPPABLE #-}
348+-- #endif
349+-- (MimeUnrender ct a) => HasReflexClient (Patch (ct ': cts) a) where
350+-- type Input (Patch (ct ': cts) a) = ()
351+-- type Client (Patch (ct ': cts) a) = Final a
352+-- clientWithRoute Proxy req baseurl _ =
353+-- snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPatch req baseurl
354+--
355+-- instance
356+-- #if MIN_VERSION_base(4,8,0)
357+-- {-# OVERLAPPING #-}
358+-- #endif
359+-- HasReflexClient (Patch (ct ': cts) ()) where
360+-- type Input (Patch (ct ': cts) ()) = ()
361+-- type Client (Patch (ct ': cts) ()) = Final ()
362+-- clientWithRoute Proxy req baseurl _ =
363+-- void $ performRequestNoBody H.methodPatch req baseurl
364+--
365+-- -- | If you have a 'Patch xs (Headers ls x)' endpoint, the client expects the
366+-- -- corresponding headers.
367+-- instance
368+-- #if MIN_VERSION_base(4,8,0)
369+-- {-# OVERLAPPING #-}
370+-- #endif
371+-- ( MimeUnrender ct a, BuildHeadersTo ls
372+-- ) => HasReflexClient (Patch (ct ': cts) (Headers ls a)) where
373+-- type Input (Patch (ct ': cts) (Headers ls a)) = ()
374+-- type Client (Patch (ct ': cts) (Headers ls a)) = Final (Headers ls a)
375+-- clientWithRoute Proxy req baseurl _ = do
376+-- (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPatch req baseurl
377+-- return $ Headers { getResponse = resp
378+-- , getHeadersHList = buildHeadersTo hdrs
379+-- }
380+--
381+-- -- | If you use a 'QueryParam' in one of your endpoints in your API,
382+-- -- the corresponding querying function will automatically take
383+-- -- an additional argument of the type specified by your 'QueryParam',
384+-- -- enclosed in Maybe.
385+-- --
386+-- -- If you give Nothing, nothing will be added to the query string.
387+-- --
388+-- -- If you give a non-'Nothing' value, this function will take care
389+-- -- of inserting a textual representation of this value in the query string.
390+-- --
391+-- -- You can control how values for your type are turned into
392+-- -- text by specifying a 'ToHttpApiData' instance for your type.
393+-- --
394+-- -- Example:
395+-- --
396+-- -- > type MyApi = "books" :> QueryParam "author" Text :> Get '[JSON] [Book]
397+-- -- >
398+-- -- > myApi :: Proxy MyApi
399+-- -- > myApi = Proxy
400+-- -- >
401+-- -- > getBooksBy :: Maybe Text -> ExceptT String IO [Book]
402+-- -- > getBooksBy = client myApi host
403+-- -- > where host = BaseUrl Http "localhost" 8080
404+-- -- > -- then you can just use "getBooksBy" to query that endpoint.
405+-- -- > -- 'getBooksBy Nothing' for all books
406+-- -- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
407+-- instance (KnownSymbol sym, ToHttpApiData a, HasReflexClient sublayout)
408+-- => HasReflexClient (QueryParam sym a :> sublayout) where
409+--
410+-- type Input (QueryParam sym a :> sublayout) = Maybe a ::> Input sublayout
411+-- type Client (QueryParam sym a :> sublayout) = Client sublayout
412+--
413+-- -- if mparam = Nothing, we don't add it to the query string
414+-- clientWithRoute Proxy req baseurl (mparam ::> rest) =
415+-- clientWithRoute (Proxy :: Proxy sublayout)
416+-- (maybe req
417+-- (flip (appendToQueryString pname) req . Just)
418+-- mparamText
419+-- )
420+-- baseurl rest
421+--
422+-- where pname = cs pname'
423+-- pname' = symbolVal (Proxy :: Proxy sym)
424+-- mparamText = fmap toQueryParam mparam
425+--
426+-- -- | If you use a 'QueryParams' in one of your endpoints in your API,
427+-- -- the corresponding querying function will automatically take
428+-- -- an additional argument, a list of values of the type specified
429+-- -- by your 'QueryParams'.
430+-- --
431+-- -- If you give an empty list, nothing will be added to the query string.
432+-- --
433+-- -- Otherwise, this function will take care
434+-- -- of inserting a textual representation of your values in the query string,
435+-- -- under the same query string parameter name.
436+-- --
437+-- -- You can control how values for your type are turned into
438+-- -- text by specifying a 'ToHttpApiData' instance for your type.
439+-- --
440+-- -- Example:
441+-- --
442+-- -- > type MyApi = "books" :> QueryParams "authors" Text :> Get '[JSON] [Book]
443+-- -- >
444+-- -- > myApi :: Proxy MyApi
445+-- -- > myApi = Proxy
446+-- -- >
447+-- -- > getBooksBy :: [Text] -> ExceptT String IO [Book]
448+-- -- > getBooksBy = client myApi host
449+-- -- > where host = BaseUrl Http "localhost" 8080
450+-- -- > -- then you can just use "getBooksBy" to query that endpoint.
451+-- -- > -- 'getBooksBy []' for all books
452+-- -- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
453+-- -- > -- to get all books by Asimov and Heinlein
454+-- instance (KnownSymbol sym, ToHttpApiData a, HasReflexClient sublayout)
455+-- => HasReflexClient (QueryParams sym a :> sublayout) where
456+--
457+-- type Input (QueryParams sym a :> sublayout) = [a] ::> Input sublayout
458+-- type Client (QueryParams sym a :> sublayout) = Client sublayout
459+--
460+-- clientWithRoute Proxy req baseurl (paramlist ::> rest) =
461+-- clientWithRoute (Proxy :: Proxy sublayout)
462+-- (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))
463+-- req
464+-- paramlist'
465+-- )
466+-- baseurl rest
467+--
468+-- where pname = cs pname'
469+-- pname' = symbolVal (Proxy :: Proxy sym)
470+-- paramlist' = map (Just . toQueryParam) paramlist
471+--
472+-- -- | If you use a 'QueryFlag' in one of your endpoints in your API,
473+-- -- the corresponding querying function will automatically take
474+-- -- an additional 'Bool' argument.
475+-- --
476+-- -- If you give 'False', nothing will be added to the query string.
477+-- --
478+-- -- Otherwise, this function will insert a value-less query string
479+-- -- parameter under the name associated to your 'QueryFlag'.
480+-- --
481+-- -- Example:
482+-- --
483+-- -- > type MyApi = "books" :> QueryFlag "published" :> Get '[JSON] [Book]
484+-- -- >
485+-- -- > myApi :: Proxy MyApi
486+-- -- > myApi = Proxy
487+-- -- >
488+-- -- > getBooks :: Bool -> ExceptT String IO [Book]
489+-- -- > getBooks = client myApi host
490+-- -- > where host = BaseUrl Http "localhost" 8080
491+-- -- > -- then you can just use "getBooks" to query that endpoint.
492+-- -- > -- 'getBooksBy False' for all books
493+-- -- > -- 'getBooksBy True' to only get _already published_ books
494+-- instance (KnownSymbol sym, HasReflexClient sublayout)
495+-- => HasReflexClient (QueryFlag sym :> sublayout) where
496+--
497+-- type Input (QueryFlag sym :> sublayout) = Bool ::> Input sublayout
498+-- type Client (QueryFlag sym :> sublayout) = Client sublayout
499+--
500+-- clientWithRoute Proxy req baseurl (flag ::> rest) =
501+-- clientWithRoute (Proxy :: Proxy sublayout)
502+-- (if flag
503+-- then appendToQueryString paramname Nothing req
504+-- else req
505+-- )
506+-- baseurl rest
507+--
508+-- where paramname = cs $ symbolVal (Proxy :: Proxy sym)
509+--
510+--
511+-- -- | If you use a 'ReqBody' in one of your endpoints in your API,
512+-- -- the corresponding querying function will automatically take
513+-- -- an additional argument of the type specified by your 'ReqBody'.
514+-- -- That function will take care of encoding this argument as JSON and
515+-- -- of using it as the request body.
516+-- --
517+-- -- All you need is for your type to have a 'ToJSON' instance.
518+-- --
519+-- -- Example:
520+-- --
521+-- -- > type MyApi = "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book
522+-- -- >
523+-- -- > myApi :: Proxy MyApi
524+-- -- > myApi = Proxy
525+-- -- >
526+-- -- > addBook :: Book -> ExceptT String IO Book
527+-- -- > addBook = client myApi host
528+-- -- > where host = BaseUrl Http "localhost" 8080
529+-- -- > -- then you can just use "addBook" to query that endpoint
530+-- instance (MimeRender ct a, HasReflexClient sublayout)
531+-- => HasReflexClient (ReqBody (ct ': cts) a :> sublayout) where
532+--
533+-- type Input (ReqBody (ct ': cts) a :> sublayout) = a ::> Input sublayout
534+-- type Client (ReqBody (ct ': cts) a :> sublayout) = Client sublayout
535+--
536+-- clientWithRoute Proxy req baseurl (body ::> rest) =
537+-- clientWithRoute (Proxy :: Proxy sublayout)
538+-- (let ctProxy = Proxy :: Proxy ct
539+-- in setRQBody (mimeRender ctProxy body)
540+-- (contentType ctProxy)
541+-- req
542+-- )
543+-- baseurl rest
544+--
545+-- -- | Make the querying function append @path@ to the request path.
546+-- instance (KnownSymbol path, HasReflexClient sublayout) => HasReflexClient (path :> sublayout) where
547+-- type Input (path :> sublayout) = Input sublayout
548+-- type Client (path :> sublayout) = Client sublayout
549+--
550+-- clientWithRoute Proxy req baseurl val =
551+-- clientWithRoute (Proxy :: Proxy sublayout)
552+-- (appendToPath p req)
553+-- baseurl val
554+--
555+-- where p = symbolVal (Proxy :: Proxy path)
556+--
557+-- instance HasReflexClient api => HasReflexClient (Vault :> api) where
558+-- type Input (Vault :> api) = Input api
559+-- type Client (Vault :> api) = Client api
560+--
561+-- clientWithRoute Proxy req baseurl val =
562+-- clientWithRoute (Proxy :: Proxy api) req baseurl val
563+--
564+-- instance HasReflexClient api => HasReflexClient (RemoteHost :> api) where
565+-- type Input (RemoteHost :> api) = Input api
566+-- type Client (RemoteHost :> api) = Client api
567+--
568+-- clientWithRoute Proxy req baseurl val =
569+-- clientWithRoute (Proxy :: Proxy api) req baseurl val
570+--
571+-- instance HasReflexClient api => HasReflexClient (IsSecure :> api) where
572+-- type Input (IsSecure :> api) = Input api
573+-- type Client (IsSecure :> api) = Client api
574+--
575+-- clientWithRoute Proxy req baseurl val =
576+-- clientWithRoute (Proxy :: Proxy api) req baseurl val