@@ -21,15 +21,36 @@ import Servant.Common.Text
21import Servant.Docs
21import Servant.Docs
22import Servant.Server
22import Servant.Server
24−-- * Single query string parameter lookup
24+-- | Lookup the value associated to the @sym@ query string parameter
25−
25+-- and try to extract it as a value of type @a@.
26−-- | You must implement:
26+--
28−-- - a @'FromText' a@ instance for serving
29+-- > -- /books?author=<author name>
29−-- - a @'ToText' a@ instance for (client-side) querying
30+-- > type MyApi = "books" :> QueryParam "author" Text :> Get [Book]
30−-- - a @'ToParam' ('QueryParam' sym a)@ instance for automatic documentation generation
31data QueryParam sym a
31data QueryParam sym a
33+-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
34+-- this automatically requires your server-side handler to be a function
35+-- that takes an argument of type @'Maybe' 'Text'@.
37+-- This lets servant worry about looking it up in the query string
38+-- and turning it into a value of the type you specify, enclosed
39+-- in 'Maybe', because it may not be there and servant would then
40+-- hand you 'Nothing'.
42+-- You can control how it'll be converted from 'Text' to your type
43+-- by simply providing an instance of 'FromText' for your type.
47+-- > type MyApi = "books" :> QueryParam "author" Text :> Get [Book]
49+-- > server :: Server MyApi
50+-- > server = getBooksBy
51+-- > where getBooksBy :: Maybe Text -> EitherT (Int, String) IO [Book]
52+-- > getBooksBy Nothing = ...return all books...
53+-- > getBooksBy (Just author) = ...return books by the given author...
33instance (KnownSymbol sym, FromText a, HasServer sublayout)
54instance (KnownSymbol sym, FromText a, HasServer sublayout)
34 => HasServer (QueryParam sym a :> sublayout) where
55 => HasServer (QueryParam sym a :> sublayout) where
@@ -49,6 +70,31 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
50 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
71 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
73+-- | If you use a 'QueryParam' in one of your endpoints in your API,
74+-- the corresponding querying function will automatically take
75+-- an additional argument of the type specified by your 'QueryParam',
78+-- If you give Nothing, nothing will be added to the query string.
80+-- If you give a non-'Nothing' value, this function will take care
81+-- of inserting a textual representation of this value in the query string.
83+-- You can control how values for your type are turned into
84+-- text by specifying a 'ToText' instance for your type.
88+-- > type MyApi = "books" :> QueryParam "author" Text :> Get [Book]
90+-- > myApi :: Proxy MyApi
93+-- > getBooksBy :: Maybe Text -> BaseUrl -> EitherT String IO [Book]
94+-- > getBooksBy = client myApi
95+-- > -- then you can just use "getBooksBy" to query that endpoint.
96+-- > -- 'getBooksBy Nothing' for all books
97+-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
52instance (KnownSymbol sym, ToText a, HasClient sublayout)
98instance (KnownSymbol sym, ToText a, HasClient sublayout)
53 => HasClient (QueryParam sym a :> sublayout) where
99 => HasClient (QueryParam sym a :> sublayout) where
@@ -74,9 +120,38 @@ instance (KnownSymbol sym, ToParam (QueryParam sym a), HasDocs sublayout)
74 paramP = Proxy :: Proxy (QueryParam sym a)
120 paramP = Proxy :: Proxy (QueryParam sym a)
75 action' = over params (|> toParam paramP) action
121 action' = over params (|> toParam paramP) action
77−-- | Retrieve a list from the query string.
123+-- | Lookup the values associated to the @sym@ query string parameter
124+-- and try to extract it as a value of type @[a]@. This is typically
125+-- meant to support query string parameters of the form
126+-- @param[]=val1¶m[]=val2@ and so on. Note that servant doesn't actually
127+-- require the @[]@s and will fetch the values just fine with
128+-- @param=val1¶m=val2@, too.
132+-- > -- /books?authors[]=<author1>&authors[]=<author2>&...
133+-- > type MyApi = "books" :> QueryParams "authors" Text :> Get [Book]
78data QueryParams sym a
134data QueryParams sym a
136+-- | If you use @'QueryParams' "authors" Text@ in one of the endpoints for your API,
137+-- this automatically requires your server-side handler to be a function
138+-- that takes an argument of type @['Text']@.
140+-- This lets servant worry about looking up 0 or more values in the query string
141+-- associated to @authors@ and turning each of them into a value of
142+-- the type you specify.
144+-- You can control how the individual values are converted from 'Text' to your type
145+-- by simply providing an instance of 'FromText' for your type.
149+-- > type MyApi = "books" :> QueryParams "authors" Text :> Get [Book]
151+-- > server :: Server MyApi
152+-- > server = getBooksBy
153+-- > where getBooksBy :: [Text] -> EitherT (Int, String) IO [Book]
154+-- > getBooksBy authors = ...return all books by these authors...
80instance (KnownSymbol sym, FromText a, HasServer sublayout)
155instance (KnownSymbol sym, FromText a, HasServer sublayout)
81 => HasServer (QueryParams sym a :> sublayout) where
156 => HasServer (QueryParams sym a :> sublayout) where
@@ -98,6 +173,33 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
98 convert Nothing = Nothing
173 convert Nothing = Nothing
99 convert (Just v) = fromText v
174 convert (Just v) = fromText v
176+-- | If you use a 'QueryParams' in one of your endpoints in your API,
177+-- the corresponding querying function will automatically take
178+-- an additional argument, a list of values of the type specified
179+-- by your 'QueryParams'.
181+-- If you give an empty list, nothing will be added to the query string.
183+-- Otherwise, this function will take care
184+-- of inserting a textual representation of your values in the query string,
185+-- under the same query string parameter name.
187+-- You can control how values for your type are turned into
188+-- text by specifying a 'ToText' instance for your type.
192+-- > type MyApi = "books" :> QueryParams "authors" Text :> Get [Book]
194+-- > myApi :: Proxy MyApi
197+-- > getBooksBy :: [Text] -> BaseUrl -> EitherT String IO [Book]
198+-- > getBooksBy = client myApi
199+-- > -- then you can just use "getBooksBy" to query that endpoint.
200+-- > -- 'getBooksBy []' for all books
201+-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
202+-- > -- to get all books by Asimov and Heinlein
101instance (KnownSymbol sym, ToText a, HasClient sublayout)
203instance (KnownSymbol sym, ToText a, HasClient sublayout)
102 => HasClient (QueryParams sym a :> sublayout) where
204 => HasClient (QueryParams sym a :> sublayout) where
@@ -122,9 +224,29 @@ instance (KnownSymbol sym, ToParam (QueryParams sym a), HasDocs sublayout)
122 paramP = Proxy :: Proxy (QueryParams sym a)
224 paramP = Proxy :: Proxy (QueryParams sym a)
123 action' = over params (|> toParam paramP) action
225 action' = over params (|> toParam paramP) action
125−-- | Retrieve a value-less boolean from the query string.
227+-- | Lookup a potentially value-less query string parameter
228+-- with boolean semantics. If the param @sym@ is there without any value,
229+-- or if it's there with value "true" or "1", it's interpreted as 'True'.
230+-- Otherwise, it's interpreted as 'False'.
234+-- > -- /books?published
235+-- > type MyApi = "books" :> QueryFlag "published" :> Get [Book]
126−data QueryFlag a
236+data QueryFlag sym
238+-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
239+-- this automatically requires your server-side handler to be a function
240+-- that takes an argument of type 'Bool'.
244+-- > type MyApi = "books" :> QueryFlag "published" :> Get [Book]
246+-- > server :: Server MyApi
247+-- > server = getBooks
248+-- > where getBooks :: Bool -> EitherT (Int, String) IO [Book]
249+-- > getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument...
128instance (KnownSymbol sym, HasServer sublayout)
250instance (KnownSymbol sym, HasServer sublayout)
129 => HasServer (QueryFlag sym :> sublayout) where
251 => HasServer (QueryFlag sym :> sublayout) where
@@ -144,6 +266,27 @@ instance (KnownSymbol sym, HasServer sublayout)
144 examine v | v == "true" || v == "1" || v == "" = True
266 examine v | v == "true" || v == "1" || v == "" = True
145 | otherwise = False
267 | otherwise = False
269+-- | If you use a 'QueryFlag' in one of your endpoints in your API,
270+-- the corresponding querying function will automatically take
271+-- an additional 'Bool' argument.
273+-- If you give 'False', nothing will be added to the query string.
275+-- Otherwise, this function will insert a value-less query string
276+-- parameter under the name associated to your 'QueryFlag'.
280+-- > type MyApi = "books" :> QueryFlag "published" :> Get [Book]
282+-- > myApi :: Proxy MyApi
285+-- > getBooks :: Bool -> BaseUrl -> EitherT String IO [Book]
286+-- > getBooks = client myApi
287+-- > -- then you can just use "getBooks" to query that endpoint.
288+-- > -- 'getBooksBy False' for all books
289+-- > -- 'getBooksBy True' to only get _already published_ books
147instance (KnownSymbol sym, HasClient sublayout)
290instance (KnownSymbol sym, HasClient sublayout)
148 => HasClient (QueryFlag sym :> sublayout) where
291 => HasClient (QueryFlag sym :> sublayout) where