h*      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                             0.20.2# Safe-Inferred*"%&()*/01379:;<> (servant=Union of two APIs, first takes precedence in case of overlap.Example::{8type MyApi = "books" :> Get '[JSON] [Book] -- GET /books :<|> "books" :> ReqBody '[JSON] Book :> Post '[JSON] () -- POST /books:}()()(3)3 Safe-Inferred'"%&()*/01379<>6servant=A simple datatype to hold data required to decorate a request:servantCombinator for  -https://tools.ietf.org/html/rfc2617#section-2Basic Access Authentication.IMPORTANT*: Only use Basic Auth over HTTPS! Credentials are not hashed or encrypted. Note also that because the same credentials are sent on every request, Basic Auth is not as secure as some alternatives. Further, the implementation in servant-server does not protect against some types of timing attacks.In Basic Auth, username and password are base64-encoded and transmitted via the  Authorization header. Handshakes are not required, making it relatively efficient.6987::6987 Safe-Inferred'"%&()*/01379<>;servantCapture all remaining values from the request path under a certain type a.Example: -- GET /src/*type MyAPI = "src" :> CaptureAll "segments" Text :> Get '[JSON] SourceFile<servant=) which can be modified. For example with  Description.=servant;Capture a value from the request path under a certain type a.Example:-- GET /books/:isbn?type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book=<;=<; Safe-Inferred'"%&()*/01379<>>servant*A type for responses without content-body.IservantInstantiate this class to register a way of deserializing a type based on the request's  Content-Type header.)import Network.HTTP.Media hiding (Accept)2import qualified Data.ByteString.Lazy.Char8 as BSC)data MyContentType = MyContentType String:{#instance Accept MyContentType where contentType _ = "example" // "prs.me.mine" /: ("charset", "utf-8"):}:{5instance Read a => MimeUnrender MyContentType a where- mimeUnrender _ bs = case BSC.take 12 bs of "MyContentType" -> return . read . BSC.unpack $ BSC.drop 12 bs8 _ -> Left "didn't start with the magic incantation":}type MyAPI = "path" :> ReqBody '[MyContentType] Int :> Get '[JSON] IntKservant"Variant which is given the actual  provided by the other party.8In the most cases you don't want to branch based on the . See  3https://github.com/haskell-servant/servant/pull/552pr552 for a motivating example.NservantInstantiate this class to register a way of serializing a type based on the Accept header.Example: data MyContentType instance Accept MyContentType where contentType _ = "example" // "prs.me.mine" /: ("charset", "utf-8") instance Show a => MimeRender MyContentType a where mimeRender _ val = pack ("This is MINE! " ++ show val) type MyAPI = "path" :> Get '[MyContentType] IntRservant Instances of R> represent mimetypes. They are used for matching against the Accept2 HTTP header of the request, and for setting the  Content-Type header of the responseExample:&import Network.HTTP.Media ((//), (/:)) data HTML:{instance Accept HTML where; contentType _ = "text" // "html" /: ("charset", "utf-8"):}Zservant$Deprecated: since aeson version 0.9  has lenient behavior.[servant application/octet-stream\servant text/plain;charset=utf-8]servant !application/x-www-form-urlencoded^servant application/json_servant`servant idaservant BC.packbservant fromStrict . TextS.encodeUtf8cservantdservanturlEncodeAsForm Note that the *mimeUnrender p (mimeRender p x) == Right x> law only holds if every element of x is non-null (i.e., not ("", ""))eservantfservant Right . toStrictgservant  Right . idhservant Right . BC.unpackiservant (left show . TextS.decodeUtf8' . toStrictjservant left show . TextL.decodeUtf8'kservanturlDecodeAsForm Note that the *mimeUnrender p (mimeRender p x) == Right x> law only holds if every element of x is non-null (i.e., not ("", ""))lservantXWVURSTNOIJK>?PQLMFGHDEBC@AZYXWVURSTNOIJK>?PQLMFGHDEBC@AZY  Safe-Inferred'"%&()*/01379<> TservantImplementation of .servantFold list of modifiers to extract description as a type-level String.:kind! FoldDescription '[]FoldDescription '[] :: Symbol= "":kind! FoldDescription '[Required, Description "foobar", Lenient]FoldDescription '[Required, Description "foobar", Lenient] :: Symbol = "foobar"servant/Add more verbose description for (part of) API.Example::{type MyApi = Description? "This comment is visible in multiple Servant interpretations \( \and can be really long if necessary. \3 \Haskell multiline String support is not perfect \ \but it's still very readable.":> Get '[JSON] Book:}servant&Add a short summary for (part of) API.Example:type MyApi = Summary "Get book by ISBN." :> "books" :> Capture "isbn" Text :> Get '[JSON] Bookservant&Reflect description to the term level.reflectDescription (Proxy :: Proxy '[Required, Description "foobar", Lenient])"foobar"  Safe-Inferred'"%&()*/01379<>!servantAn empty API: one which serves nothing. Morally speaking, this should be the unit of :<|>=. Implementors of interpretations of API types should treat " as close to the unit as possible.  Safe-Inferred'"%&()*/01379<>"servantA generalized Authentication combinator. Use this if you have a non-standard authentication technique.5NOTE: THIS API IS EXPERIMENTAL AND SUBJECT TO CHANGE. Safe-Inferred'"%&()*/01379<>#servant=Document the URI fragment in API. Useful in combination with Link.Example:-- /post#TRACKING Fragment Text :> Get '[JSON] Tracking Safe-Inferred'"%&()*/01379<>'oservantA type that specifies that an API record contains an API definition. Only useful at type-level.servant,Turns a generic product type into a tree of ( combinators.servantA class with a type family that applies an appropriate type family to the api parameter. For example,  will leave api untouched, while  AsServerT m will produce ServerT api m.servant"A constraint alias, for work with mode and routes.servantSee , but at value-level.servant Inverse of .This can be used to turn  generated. values such as client functions into records.1You may need to provide a type signature for the output type (your record type).servantGet a  of an API type.  0 Safe-Inferred'"%&()*/01379<>(  Safe-Inferred'"%&()*/01379<>*servant-Was this request made over an SSL connection?Note that this value will not tell you if the client originally made this request over SSL, but rather whether the current connection is SSL. The distinction lies with reverse proxies. In many cases, the client will connect to a load balancer over SSL, but connect to the WAI handler without SSL. In such a case, the handlers would get =, but from a user perspective, there is a secure connection.servant1the connection to the server is secure (HTTPS)servant4the connection to the server is not secure (HTTP)  Safe-Inferred'"%&()*/01379<>1p servantHelper type alias.By default argument is  and .,  C a,  C   a,  C  a,  C  (  a)servantHelper type alias. C a C  aservantImplementation of .servantFold modifier list to decide whether argument should be parsed strictly or leniently.:kind! FoldLenient '[]FoldLenient '[] :: Bool= 'Falseservant&Strictly parsed argument. Not wrapped.servant?Leniently parsed argument, i.e. parsing never fail. Wrapped in  .servantImplementation of .servant:Fold modifier list to decide whether argument is required.8:kind! FoldRequired '[Required, Description "something"]9FoldRequired '[Required, Description "something"] :: Bool= 'True):kind! FoldRequired '[Required, Optional]*FoldRequired '[Required, Optional] :: Bool= 'False:kind! FoldRequired '[]FoldRequired '[] :: Bool= 'FalseservantOptional argument. Wrapped in .servantRequired argument. Not wrapped.servantFold a RequiredAgument into a valueservantUnfold a value into a .servantUnfold a value into a .servantservantservanterror when argument is requiredservant&error when argument is strictly parsedservantvalueservanterror when argument is requiredservant&error when argument is strictly parsedservantvalue   Safe-Inferred'"%&()*/01379<>3/servant4Extract the given header's value as a value of type a0. I.e. header sent by client, parsed by server.Example:2newtype Referer = Referer Text deriving (Eq, Show)-- GET /view-my-referertype MyApi = "view-my-referer" :> Header "from" Referer :> Get '[JSON] Referer  Safe-Inferred'"%&()*/01379<>3servantCombinator for embedding a record of named routes into a Servant API type.! Safe-Inferred'"%&()*/01379<>91servantLookup a potentially value-less query string parameter with boolean semantics. If the param sym is there without any value, or if it's there with value "true" or "1", it's interpreted as ". Otherwise, it's interpreted as .Example:-- /books?publishedtype MyApi = "books" :> QueryFlag "published" :> Get '[JSON] [Book]servant$Lookup the values associated to the sym query string parameter and try to extract it as a value of type [a]. This is typically meant to support query string parameters of the form param[]=val1¶m[]=val2< and so on. Note that servant doesn't actually require the [],s and will fetch the values just fine with param=val1¶m=val2, too.Example:5-- /books?authors[]=&authors[]=&...type MyApi = "books" :> QueryParams "authors" Text :> Get '[JSON] [Book]servant which can be , , or modified otherwise.servant#Lookup the value associated to the sym query string parameter and try to extract it as a value of type a.Example:-- /books?author=type MyApi = "books" :> QueryParam "author" Text :> Get '[JSON] [Book] Safe-Inferred'"%&()*/01379<>>servantGenerate query parameters from an object, using the deep object syntax. A result of '(["a", "b", "c"], Just "d")' attributed to the filter parameter name will result in the following query parameter: filter[a][b][c]=dservantExtract a deep object from (possibly nested) query parameters. a param like filter[a][b][c]=d will be represented as '(["a", "b", "c"], Just "d")';. Note that a parameter with no nested field is possible: filter=a will be represented as '([], Just "a")'servant+Extract an deep object from a query string.Example:-- /books?filter[author][name]=&filter[year]=type MyApi = "books" :> DeepQuery "filter" BookQuery :> Get '[JSON] [Book]servantExtract the whole query string from a request. This is useful for query strings containing dynamic parameter names. For query strings with static parameter names,  QueryParam is more suited.Example:/-- /books?author=&year=9type MyApi = "books" :> QueryString :> Get '[JSON] [Book]servant1Turn a nested path into a deep object query param6generateDeepParam "filter" (["a", "b", "c"], Just "d")("filter[a][b][c]",Just "d")" Safe-Inferred'"%&()*/01379<>Aservant Variant of  that lets you access the underlying monadic context to process the request.servant&Endpoint for plugging in your own Wai  Applications. The given  Application will get the request as received by the server, potentially with a modified (stripped) pathInfo if the  Application is being routed with #$.:In addition to just letting you plug in your existing WAI  Application/s, this can also be used with functions from  https://hackage.haskell.org/package/servant-server/docs/Servant-Server-StaticFiles.htmlServant.Server.StaticFiles to serve static files stored in a particular directory on your filesystem Safe-Inferred'"%&()*/01379<>BservantProvides access to the host or IP address from which the HTTP request was sent.% Safe-Inferred'"%&()*/01379<>CservantNote:  is always .servant,Extract the request body as a value of type a.Example:-- POST /bookstype MyApi = "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book Safe-Inferred'"%&()*/01379<>DservantWitness that a type-level natural number corresponds to a HTTP status codeservant2Retrieve a known or unknown Status from a KnownNat# Safe-Inferred'"%&()*/01379<>FOservant7The contained API (second argument) can be found under  ("/" ++ path)" (path being the first argument).Example:-- GET /hello/world'-- returning a JSON encoded World value4type MyApi = "hello" :> "world" :> Get '[JSON] World4 Safe-Inferred'"%&()*/01379<>GservantNo instance exists for tycls (expr :> ...) because expr is not fully saturated.servantNo instance exists for expr.servantNo instance exists for tycls (expr :> ...) because expr is not recognised. Safe-Inferred'"%&()*/01379<>K'servantCheck whether all values in a type-level list are distinct. This will throw a nice error if there are any duplicate elements in the list.servantCheck whether a is in given type-level list. This will throw a nice error if the element is not in the list.servantConvenience function to apply a function to an unknown union element using a type class. All elements of the union must have instances in the type class, and the function is applied unconditionally. See also: .servant6Convenience function to extract a union element using cast, ie. return the value if the selected type happens to be the actual type of the union in this value, or  otherwise. See also: . Safe-Inferred'"%&()*/01379<>Up servantAuxiliary class for  ( hs a) instanceservantAuxiliary class for  ( hs) instanceservantResponse Header objects. You should never need to construct one directly. Instead, use .servantThe underlying value of a servantHList of headers.servant addHeader adds a header to a response. Note that it changes the type of the value in the following ways: .A simple value is wrapped in "Headers '[hdr]":let example0 = addHeader 5 "hi" :: Headers '[Header "someheader" Int] String;getHeaders example0[("someheader","5")] A value that already has a header has its new header *prepended* to the existing list:let example1 = addHeader 5 "hi" :: Headers '[Header "someheader" Int] String;let example2 = addHeader True example1 :: Headers '[Header "1st" Bool, Header "someheader" Int] StringgetHeaders example2#[("1st","true"),("someheader","5")]Note that while in your handlers type annotations are not required, since the type can be inferred from the API type, in other cases you may find yourself needing to add annotations.servantSame as  but works with , so it's possible to use any mods.servant,Deliberately do not add a header to a value.let example1 = noHeader "hi" :: Headers '[Header "someheader" Int] StringgetHeaders example1[]servantSame as  but works with , so it's possible to use any mods.servantLook up a specific ResponseHeader, without having to know what position it is in the HList.let example1 = addHeader 5 "hi" :: Headers '[Header "someheader" Int] Stringlet example2 = addHeader True example1 :: Headers '[Header "1st" Bool, Header "someheader" Int] StringlookupResponseHeader example2 :: ResponseHeader "someheader" IntHeader 5:lookupResponseHeader example2 :: ResponseHeader "1st" Bool Header TrueUsage of this function relies on an explicit type annotation of the header to be looked up. This can be done with type annotations on the result, or with an explicit type application. In this example, the type of header value is determined by the type-inference, we only specify the name of the header::set -XTypeApplicationscase lookupResponseHeader @"1st" example2 of { Header b -> b ; _ -> False }Trueservant This instance is an optimisationservant3N.B.: The same header can't be added multiple times Safe-Inferred'"%&()*/01379<>[servant A variant of Verb that can have any of a number of response values and status codes.%FUTUREWORK: it would be nice to make Verb a special case of  , and only write instances for  HasServer etc. for the latter, getting them for the former for free. Something like: type Verb method statusCode contentTypes a = UVerb method contentTypes [WithStatus statusCode a]Backwards compatibility is tricky, though: this type alias would mean people would have to use respond instead of  or 1, so all old handlers would have to be rewritten.servantA simple newtype wrapper that pairs a type with its status code. It implements all the content types that Servant ships with by default.servantIf an API can respond with > we assume that this will happen with the status code 204 No Content. If this needs to be overridden,  can be used.servantan instance of this typeclass assigns a HTTP status code to a return typeExample:  data NotFoundError = NotFoundError String instance HasStatus NotFoundError where type StatusOf NotFoundError = 404 /You can also use the convience newtype wrapper ! if you want to avoid writing a  instance manually. It also has the benefit of showing the status code in the type; which might aid in readability.  Safe-Inferred'"%&()*/01379<>\0'' Safe-Inferred'"%&()*/01379<>c.servant with 206 status code.servant with 205 status code.servant with 205 status code.servant with 205 status code.servant with 205 status code.servant with 205 status code.servant with 204 status code.servant with 204 status code.servant with 204 status code.servant with 204 status code.servant with 204 status code.servant with 204 status code.servant with 203 status code.servant with 203 status code.servant with 203 status code.servant with 203 status code.servant with 203 status code.servant with 202 status code.servant with 202 status code.servant with 202 status code.servant with 202 status code.servant with 202 status code.servant with 201 status code.servant with 201 status code.servant with 200 status code.servant with 200 status code.servant with 200 status code.servant with 200 status code.servant with 200 status code.servant NoContentVerb! is a specific type to represent  NoContent responses. It does not require either a list of content types (because there's no content) or a status code (because it should always be 204).servantVerb is a general type for representing HTTP verbs (a.k.a. methods). For convenience, type synonyms for each verb with a 200 response code are provided, but you are free to define your own:;type Post204 contentTypes a = Verb 'POST 204 contentTypes a+ +  Safe-Inferred)"%&()*/01379<>qservantIf there is more than one fragment in an API endpoint, a compile-time error is raised.type FailAPI = Fragment Bool :> Fragment Int :> Get '[JSON] NoContent"instance AtMostOneFragment FailAPI...3...Only one Fragment allowed per endpoint in api......servantIf both a or b: produce an empty constraint, produce an empty constraint.servant If either a or b: produce an empty constraint, produce an empty constraint.servant+Check that a value is an element of a list:,ok (Proxy :: Proxy (Elem Bool '[Int, Bool]))OK.ok (Proxy :: Proxy (Elem String '[Int, Bool]))...... [Char]...'[Int, Bool......servantAppend two type-level lists.servantApply (e :>) to every API in xs.servantCheck that every element of xs is an endpoint of api (using ).=ok (Proxy :: Proxy (AllIsIn (Endpoints SampleAPI) SampleAPI))OKservantCheck whether sub is a sub API of api.Like  , but uses  rather than .servantClosed type family, check if endpoint is exactly within api.ok (Proxy :: Proxy (IsIn ("hello" :> Get '[JSON] Int) SampleAPI))OKUnlike !, this requires an *exact* match.ok (Proxy :: Proxy (IsIn (Get '[JSON] Int) (Header "h" Bool :> Get '[JSON] Int)))...... Could not ......servantCheck that every element of xs is an endpoint of api (using ).servantCheck whether sub is a sub-API of api.ok (Proxy :: Proxy (IsSubAPI SampleAPI (SampleAPI :<|> Get '[JSON] Int)))OKok (Proxy :: Proxy (IsSubAPI (SampleAPI :<|> Get '[JSON] Int) SampleAPI))...... Could not ...... This uses IsElem0 for checking; thus the note there applies here.servantClosed type family, check if endpoint is within api. Uses " if it exhausts all other options.ok (Proxy :: Proxy (IsElem ("hello" :> Get '[JSON] Int) SampleAPI))OKok (Proxy :: Proxy (IsElem ("bye" :> Get '[JSON] Int) SampleAPI))...... Could not ......An endpoint is considered within an api even if it is missing combinators that don't affect the URL:ok (Proxy :: Proxy (IsElem (Get '[JSON] Int) (Header "h" Bool :> Get '[JSON] Int)))OKok (Proxy :: Proxy (IsElem (Get '[JSON] Int) (ReqBody '[JSON] Bool :> Get '[JSON] Int)))OKN.B.:*  IsElem a b can be seen as capturing the notion of whether the URL represented by a$ would match the URL represented by b*, *not* whether a request represented by a matches the endpoints serving b (for the latter, use ).servantYou may use this type family to tell the type checker that your custom type may be skipped as part of a link. This is useful for things like  that are optional in a URI and do not affect them if they are omitted.data CustomThing7type instance IsElem' e (CustomThing :> s) = IsElem e s Note that 0 is called, which will mutually recurse back to ) if it exhausts all other options again.Once you have written a HasLink instance for  CustomThing you are ready to go.servant%Flatten API into a list of endpoints.Refl :: Endpoints SampleAPI :~: '["hello" :> Verb 'GET 200 '[JSON] Int, "bye" :> (Capture "name" String :> Verb 'POST 200 '[JSON, PlainText] Bool)]Refl Safe-Inferred'"%&()*/01379<>tLservant names a specific tagged context to use for the combinators in the API. (See also in servant-server, Servant.Server.Context.) For example: type UseNamedContextAPI = WithNamedContext "myContext" '[String] ( ReqBody '[JSON] Int :> Get '[JSON] Int) Both the ReqBody and Get combinators will use the - with type tag "myContext" as their context.Contexts are only relevant for servant-server.'For more information, see the tutorial. Safe-Inferred'"%&()*/01379<>t Safe-Inferred("%&()*/01379<>~servantListT with additional constructors.servantThis is CPSised ListT.servantCreate  from Step.Note: often enough you want to use  directly.servant Create pure .%source "foo" :: SourceT Identity CharfromStepT (Effect (Identity (Yield 'f' (Yield 'o' (Yield 'o' Stop)))))servantGet the answers.2runSourceT (source "foo" :: SourceT Identity Char) ExceptT (Identity (Right "foo")),runSourceT (source "foo" :: SourceT [] Char)ExceptT [Right "foo"]servantFilter values.toList $ mapMaybe (\x -> if odd x then Just x else Nothing) (source [0..10]) :: [Int] [1,3,5,7,9]mapMaybe (\x -> if odd x then Just x else Nothing) (source [0..2]) :: SourceT Identity Int:fromStepT (Effect (Identity (Skip (Yield 1 (Skip Stop)))))Illustrates why we need .servant!Run action for each value in the .-foreach fail print $ source ("abc" :: String)'a''b''c'servantSee .servant Read file.1foreach fail BS.putStr (readFile "servant.cabal")cabal-version: 3.0name: servant...servantTransform using  attoparsec parser.Note: parser should not accept empty input!let parser = A.skipWhile A8.isSpace_w8 >> A.takeWhile1 A8.isDigit_w8runExcept $ runSourceT $ transformWithAtto parser (source $ [fromString "1 2 3"])Right ["1","2","3"]runExcept $ runSourceT $ transformWithAtto parser (source $ map fromString ["1", "2", "3"]) Right ["123"]runExcept $ runSourceT $ transformWithAtto parser (source $ map fromString ["1", "2 3", "4"])Right ["12","34"]runExcept $ runSourceT $ transformWithAtto parser (source [fromString "foobar"])!Left "Failed reading: takeWhile1"servantDoesn't generate  constructors.servantmempty :: StepT [] IntStopmempty :: StepT Identity IntStopservantDoesn't generate  constructors.  doesn't shrink.servantmempty :: SourceT Maybe IntfromStepT (Effect (Just Stop))servant2source "xy" <> source "z" :: SourceT Identity CharfromStepT (Effect (Identity (Yield 'x' (Yield 'y' (Yield 'z' Stop)))))servant?hoist (Just . runIdentity) (source [1..3]) :: SourceT Maybe Int servant3The netstring framing strategy as defined by djb: $http://cr.yp.to/proto/netstrings.txt,Any string of 8-bit bytes may be encoded as [len]":"[string]"," . Here [string] is the string and [len]> is a nonempty sequence of ASCII digits giving the length of [string]" in decimal. The ASCII digits are  30 for 0,  31 for 1, and so on up through  39% for 9. Extra zeros at the front of [len] are prohibited: [len] begins with  30 exactly when [string] is empty.For example, the string "hello world!" is encoded as  31,32 3a 68 65 6c 6c 6f 20 77 6f 72 6c 64 21 2c , i.e., "12:hello world!,"". The empty string is encoded as "0:,".servantA simple framing strategy that has no header, and inserts a newline character after each frame. This assumes that it is used with a Content-Type that encodes without newlines (e.g. JSON).servantA framing strategy that does not do any framing at all, it just passes the input data This will be used most of the time with binary data, such as filesservantThe : class provides the logic for parsing a framing strategy.servantThe  class provides the logic for emitting a framing strategy. The strategy transforms a  m a into  m 4, therefore it can prepend, append and intercalate framing structure around chunks.Note: as the  m) is generic, this is pure transformation.servant is intended to be implemented for types such as Conduit, Pipe, etc. By implementing this class, all such streaming abstractions can be used directly on the client side for talking to streaming endpoints.servantAuxiliary class for  x ( m x) instance.servant is intended to be implemented for types such as Conduit, Pipe, etc. By implementing this class, all such streaming abstractions can be used directly as endpoints.servant3Stream endpoints may be implemented as producing a  chunk.Clients reading from streaming endpoints can be implemented as consuming a  chunk.servantA stream request body.servantA Stream endpoint for a given method emits a stream of encoded values at a given  Content-Type, delimited by a framing< strategy. Type synonyms are provided for standard methods.servant$Relax to use auxiliary class, have mservantAs  doesn't have frame separators, we take the chunks as given and try to convert them one by one.That works well when a is a  ByteString. Safe-Inferred)"%&()*/01379<>)servant#Construct a toLink for an endpoint.servantA type that specifies that an API record contains a set of links.servant#How to encode array query elements.servant foo[]=1&foo[]=2servant  foo=1&foo=2servantQuery parameter.servant6A safe link datatype. The only way of constructing a  is using , which means any / is guaranteed to be part of the mentioned API.servant Transform  into .)type API = "something" :> Get '[JSON] Int QueryParams "x" Int :> Get '[JSON] IntlinkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x[]=1&x[]=2&x[]=3'type API = "foo/bar" :> Get '[JSON] Int Capture "email" String :> Put '[JSON] ()(let someRoute = Proxy :: Proxy SomeRoute/safeLink someRoute someRoute "test@example.com"Link {_segments = ["abc","test%40example.com"], _queryParams = [], _fragment = Nothing}9linkURI $ safeLink someRoute someRoute "test@example.com"abc/test%40example.comservant Configurable .:type API = "sum" :> QueryParams "x" Int :> Get '[JSON] IntlinkURI' LinkArrayElementBracket $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x[]=1&x[]=2&x[]=3linkURI' LinkArrayElementPlain $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x=1&x=2&x=3servantCreate a valid (by construction) relative URI with query params.%This function will only typecheck if endpoint is part of the API apiservant More general .servantCreate all links in an API.Note that the api type must be restricted to the endpoints that have valid links to them.type API = "foo" :> Capture "name" Text :> Get '[JSON] Text :<|> "bar" :> Capture "name" Int :> Get '[JSON] Double8let fooLink :<|> barLink = allLinks (Proxy :: Proxy API) :t fooLinkfooLink :: Text -> Link :t barLinkbarLink :: Int -> Link4Note: nested APIs don't work well with this approach:kind! MkLink (Capture "nest" Char :> (Capture "x" Int :> Get '[JSON] Int :<|> Capture "y" Double :> Get '[JSON] Double)) LinkMkLink (Capture "nest" Char :> (Capture "x" Int :> Get '[JSON] Int :<|> Capture "y" Double :> Get '[JSON] Double)) Link :: Type-= Char -> (Int -> Link) :<|> (Double -> Link)servant More general . See .servantGiven an API record field, create a link for that route. Only the field's type is used. data Record route = Record { _get :: route :- Capture "id" Int :> Get '[JSON] String , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool } deriving ($) getLink :: Int -> Link getLink =  _get servantMore general version of servantGet all links as a record.servantMore general version of .servantHelper for implementing / for combinators not affecting link structure.servant+The API endpoint you would like to point toservant-The whole API that this endpoint is a part ofservant+The API endpoint you would like to point toservant-The whole API that this endpoint is a part ofservant+The API endpoint you would like to point to1 !  ! Safe-Inferred'"%&()*/01379<>()=<;' :6789RSTVXNOIJK>?UW  !$%&"#()=<;' :6789RSTVXNOIJK>?UW  !$%&"# Safe-Inferred'"%&()*/01379<>Iservant :: API -> API), so we have linear structure of the API.  &'(&')&*+,-.,-/,-0,-1,-2,-3,-4,-5,-6,-789:89;89<89=89>89?89@89A89B89C8DE8DE8DF8DGHIJHIJHIKHILHIMHINHIOPQRPQSPQTPQUPQVWXYZZ[\]^_`abcdefgghijklmnnopqrstuvwxyz{|}~                                  !!!!""%%#$;&&&&&&&$servant-0.20.2-Gi9sNeCszk4lB7Qo1IEZKServant.API.Generic Servant.APIServant.API.VerbsServant.API.HttpVersionServant.API.VaultServant.API.AlternativeServant.API.BasicAuthServant.API.ContentTypesServant.API.DescriptionServant.API.EmptyServant.API.Experimental.AuthServant.API.IsSecureServant.API.ModifiersServant.API.QueryStringServant.API.RemoteHostServant.API.StatusServant.API.TypeErrorsServant.API.UVerb.UnionServant.API.ResponseHeadersServant.API.UVerbServant.API.TypeLevelServant.API.WithNamedContextServant.API.WithResourceServant.Types.SourceTServant.API.Stream Servant.LinksServant.Test.ComprehensiveAPIservantServant.API.CaptureServant.API.FragmentServant.API.HeaderServant.API.NamedRoutesServant.API.QueryParamServant.API.RawServant.API.Sub:>Servant.API.ReqBodybase GHC.GenericsGenericRepData.Type.BoolIf*http-api-data-0.6.1-E7QL1R3XhNE9PjOCfDEArlWeb.Internal.HttpApiDataFromHttpApiData parseUrlPiece parseHeaderparseQueryParam ToHttpApiData toUrlPiecetoEncodedUrlPiecetoHeader toQueryParamtoEncodedQueryParam(http-types-0.12.4-3cKid6j5VJVJ0dTLPi8qAtNetwork.HTTP.Types.Method StdMethodGETPOSTHEADPUTDELETETRACECONNECTOPTIONSPATCHNetwork.HTTP.Types.Version HttpVersion httpMajor httpMinor*network-uri-2.6.4.2-EPtOLxEBFZY6ZEPq9UBoFy Network.URIURI uriScheme uriAuthorityuriPathuriQuery uriFragment+singleton-bool-0.1.8-1v3O2V7JwMnDmwAoiQk7x1Data.Singletons.BoolSBoolIsboolSBoolSTrueSFalse$vault-0.3.1.5-Jo7sODJAx1p9tDtksZcWAQData.Vault.LazyVault:<|>$fBitraversable:<|>$fBiapplicative:<|>$fBifunctor:<|>$fBifoldable:<|> $fMonoid:<|>$fSemigroup:<|>$fEq:<|> $fShow:<|> $fFunctor:<|>$fTraversable:<|>$fFoldable:<|> $fBounded:<|> BasicAuthData$$sel:basicAuthUsername:BasicAuthData$$sel:basicAuthPassword:BasicAuthData BasicAuth CaptureAllCapture'Capture NoContentAllMimeUnrenderallMimeUnrender AllMimeRender allMimeRenderAllMimeallMime AllCTUnrendercanHandleCTypeH handleCTypeH MimeUnrender mimeUnrendermimeUnrenderWithType AllCTRender handleAcceptH MimeRender mimeRender AcceptHeaderAccept contentType contentTypes OctetStreamFormUrlEncoded PlainTextJSONcanHandleAcceptHeitherDecodeLenient$fAcceptTYPEOctetStream$fAcceptTYPEPlainText$fAcceptTYPEFormUrlEncoded$fAcceptTYPEJSON%$fMimeRenderTYPEOctetStreamByteString&$fMimeRenderTYPEOctetStreamByteString0$fMimeRenderTYPEPlainTextList$fMimeRenderTYPEPlainTextText$fMimeRenderTYPEPlainTextText0$fMimeRenderTYPEFormUrlEncodeda$fMimeRenderTYPEJSONa'$fMimeUnrenderTYPEOctetStreamByteString($fMimeUnrenderTYPEOctetStreamByteString0$fMimeUnrenderTYPEPlainTextList$fMimeUnrenderTYPEPlainTextText $fMimeUnrenderTYPEPlainTextText0!$fMimeUnrenderTYPEFormUrlEncodeda$fMimeUnrenderTYPEJSONa $fAllMime: $fAllMime[]$fAllCTRender[]()$fAllMimeRender:a$fAllMimeRender:a0$fAllCTRender:a$fAllMimeUnrender:a$fAllMimeUnrender[]a$fAllCTUnrenderctypsa$fNFDataNoContent$fAllMimeRender:NoContent$fAllMimeRender:NoContent0$fShowNoContent $fEqNoContent$fReadNoContent$fGenericNoContent$fEqAcceptHeader$fShowAcceptHeader$fReadAcceptHeader$fGenericAcceptHeaderFoldDescription'FoldDescription DescriptionSummaryreflectDescriptionEmptyAPI $fEqEmptyAPI$fShowEmptyAPI$fBoundedEmptyAPI$fEnumEmptyAPI AuthProtectFragmentGServantProductAsApi ToServantApi ToServant GenericMode:-GenericServant toServant fromServant genericApi$fGenericModeTYPEAsApi$fGServantProductkK1$fGServantProductk:*:$fGServantProductkM1IsSecureSecure NotSecure $fEqIsSecure$fShowIsSecure$fReadIsSecure$fGenericIsSecure $fOrdIsSecureRequestArgumentRequiredArgument FoldLenient' FoldLenientStrictLenient FoldRequired' FoldRequiredOptionalRequiredfoldRequiredArgumentunfoldRequiredArgumentunfoldRequestArgumentHeader'Header NamedRoutes QueryFlag QueryParams QueryParam' QueryParam ToDeepQuery toDeepQuery FromDeepQuery fromDeepQuery DeepQuery QueryStringgenerateDeepParam$fFromDeepQueryMapRawMRaw RemoteHostReqBody'ReqBody KnownStatus statusVal statusFromNat$fKnownStatus511$fKnownStatus505$fKnownStatus504$fKnownStatus503$fKnownStatus502$fKnownStatus501$fKnownStatus500$fKnownStatus431$fKnownStatus429$fKnownStatus428$fKnownStatus426$fKnownStatus422$fKnownStatus418$fKnownStatus417$fKnownStatus416$fKnownStatus415$fKnownStatus414$fKnownStatus413$fKnownStatus412$fKnownStatus411$fKnownStatus410$fKnownStatus409$fKnownStatus408$fKnownStatus407$fKnownStatus406$fKnownStatus405$fKnownStatus404$fKnownStatus403$fKnownStatus402$fKnownStatus401$fKnownStatus400$fKnownStatus308$fKnownStatus307$fKnownStatus305$fKnownStatus304$fKnownStatus303$fKnownStatus302$fKnownStatus301$fKnownStatus300$fKnownStatus206$fKnownStatus205$fKnownStatus204$fKnownStatus203$fKnownStatus202$fKnownStatus201$fKnownStatus200$fKnownStatus101$fKnownStatus100ErrorIfNoGenericPartialApplication NoInstanceForNoInstanceForSubUniqueinjectejectIsMemberUnion foldMapUnion matchUnion $fUElemax: $fUElemax:0HasResponseHeader AddHeader GetHeaders' GetHeaders getHeadersBuildHeadersTobuildHeadersTo HeaderValMapHListHNilHConsResponseHeader MissingHeaderUndecodableHeaderHeaders$sel:getResponse:Headers$sel:getHeadersHList:Headers addHeader addHeader'noHeader noHeader'lookupResponseHeader$fNFDataResponseHeader $fNFDataHList$fNFDataHList:$fNFDataHList[]$fNFDataHeaders$fBuildHeadersTo:$fBuildHeadersTo[]$fGetHeadersFromHList:$fGetHeadersFromHList[]$fGetHeadersHList$fGetHeaders':$fGetHeaders'[]$fGetHeadersHeaders$fAddHeadermodshvNSNS$fAddHeadermodshvNSNS0$fAddHeadermodshvanew$fAddHeadermodshvHeadersHeaders$fHasResponseHeaderha:$fHasResponseHeaderha:0$fFunctorHeaders$fEqResponseHeader$fShowResponseHeader$fFunctorResponseHeaderUVerb WithStatus HasStatusesStatusesstatuses HasStatusStatusOfstatusOf$fHasStatusHeaders$fHasStatusNoContent$fHasStatuses:$fHasStatuses[]'$fMimeUnrenderTYPEOctetStreamWithStatus*$fMimeUnrenderTYPEFormUrlEncodedWithStatus%$fMimeUnrenderTYPEPlainTextWithStatus $fMimeUnrenderTYPEJSONWithStatus%$fMimeRenderTYPEOctetStreamWithStatus($fMimeRenderTYPEFormUrlEncodedWithStatus#$fMimeRenderTYPEPlainTextWithStatus$fMimeRenderTYPEJSONWithStatus$fHasStatusWithStatus$fEqWithStatus$fShowWithStatus ReflectMethod reflectMethodGetPartialContentPutResetContentPatchResetContentDeleteResetContentPostResetContentGetResetContent HeadNoContent PutNoContentPatchNoContentDeleteNoContent PostNoContent GetNoContentPutNonAuthoritativePatchNonAuthoritativeDeleteNonAuthoritativePostNonAuthoritativeGetNonAuthoritative PutAccepted PatchAcceptedDeleteAccepted PostAccepted GetAccepted PutCreated PostCreatedPatchDeletePutPostGet NoContentVerbVerb$fReflectMethodStdMethodCONNECT$fReflectMethodStdMethodTRACE$fReflectMethodStdMethodOPTIONS$fReflectMethodStdMethodHEAD$fReflectMethodStdMethodPATCH$fReflectMethodStdMethodDELETE$fReflectMethodStdMethodPUT$fReflectMethodStdMethodPOST$fReflectMethodStdMethodGET$fGenericNoContentVerb $fGenericVerbFragmentUniqueAtMostOneFragmentAndOrElemGoElem IsSubList AppendListMapSubAllIsInIsStrictSubAPIIsIn AllIsElemIsSubAPIIsElemIsElem' Endpoints$fAtMostOneFragmentFragment$fAtMostOneFragmentUVerb$fAtMostOneFragmentVerbWithNamedContext WithResourceStepTStopErrorSkipYieldEffectSourceT$sel:unSourceT:SourceTmapStepT fromStepTsource runSourceTrunStepTmapMaybe mapMaybeStepforeach foreachStep fromActionfromActionStepreadFiletransformWithAttotransformStepWithAtto$fArbitraryStepT $fMonoidStepT$fSemigroupStepT$fMFunctorTYPEStepT $fShowStepT $fShow1StepT$fFoldableStepT$fArbitrarySourceT$fMonoidSourceT$fSemigroupSourceT$fMFunctorTYPESourceT $fShowSourceT$fShow1SourceT$fFoldableSourceT$fFunctorSourceT$fFunctorStepTNetstringFramingNewlineFraming NoFramingFramingUnrenderframingUnrender FramingRender framingRender FromSourceIO fromSourceIOSourceToSourceIOsourceToSourceIO ToSourceIO toSourceIOSourceIO StreamBody' StreamBody StreamPost StreamGetStream$fToSourceIOaList$fToSourceIOaNonEmpty$fToSourceIOchunkSourceT$fSourceToSourceIOIO$fFromSourceIOaSourceT$fFramingUnrenderTYPENoFraming$fFramingRenderTYPENoFraming#$fFramingUnrenderTYPENewlineFraming!$fFramingRenderTYPENewlineFraming%$fFramingUnrenderTYPENetstringFraming#$fFramingRenderTYPENetstringFraming$fGenericStreamBody'$fGenericStreamHasLinkMkLinktoLinkAsLinkLinkArrayElementStyleLinkArrayElementBracketLinkArrayElementPlainParam SingleParamArrayElemParam FlagParamLink linkSegmentslinkQueryParams linkFragmentlinkURIlinkURI'safeLink safeLink'allLinks allLinks' fieldLink fieldLink' allFieldLinksallFieldLinks' $fShowEscaped$fToHttpApiDataLink $fHasLinkkapi$fHasLinkTYPE:>$fHasLinkTYPE:>0$fHasLinkTYPE:>1$fHasLinkTYPE:>2$fHasLinkTYPEUVerb$fHasLinkTYPEStream$fHasLinkTYPERawM$fHasLinkTYPERaw$fHasLinkTYPENoContentVerb$fHasLinkTYPEVerb$fHasLinkTYPEEmptyAPI$fHasLinkTYPE:>3$fHasLinkTYPE:>4$fHasLinkTYPE:>5$fHasLinkTYPEWithNamedContext$fHasLinkTYPE:>6$fHasLinkTYPE:>7$fHasLinkTYPE:>8$fHasLinkTYPE:>9$fHasLinkTYPE:>10$fHasLinkTYPE:>11$fHasLinkTYPE:>12$fHasLinkTYPE:>13$fHasLinkTYPE:>14$fHasLinkTYPE:>15$fHasLinkTYPE:<|>$fHasLinkTYPE:>16$fHasLinkTYPE:>17$fHasLinkTYPE:>18$fHasLinkTYPE:>19$fGenericModeTYPEAsLink$fHasLinkTYPENamedRoutes$fGLinkroutesa$fEqLinkArrayElementStyle$fOrdLinkArrayElementStyle$fShowLinkArrayElementStyle$fEnumLinkArrayElementStyle$fBoundedLinkArrayElementStyle $fShowLink $fShowParam%ComprehensiveAPIWithoutStreamingOrRaw&ComprehensiveAPIWithoutStreamingOrRaw' ComprehensiveAPIWithoutStreamingComprehensiveAPIWithoutRaw EmptyEndpointStreamingEndpoint RawEndpointComprehensiveAPIcomprehensiveAPIcomprehensiveAPIWithoutRaw comprehensiveAPIWithoutStreaming%comprehensiveAPIWithoutStreamingOrRaw)http-media-0.8.1.1-25SvPVwo5uwIbsI6nYuEWo%Network.HTTP.Media.MediaType.Internal MediaType$aeson-2.2.3.0-G9Z2Jit3h8hFb5zDbTBNRhData.Aeson.Decoding eitherDecodebytestring-0.11.5.2Data.ByteString.Lazy.Internal fromStrict text-2.0.2Data.Text.Lazy.Encoding encodeUtf8 Data.Aesonencode Data.ProxyProxy Data.EitherEitherData.Text.InternalText GHC.MaybeMaybeghc-prim GHC.TypesTrueFalseCheckElemIsMemberNothingGetHeadersFromHListaddOptionalHeaderGHC.Basepurereturn ByteStringMonad simpleToLink