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
:: a typeCtrl KGHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05
Moduleservant-0.20.2Haskell2010
This module collects utilities for manipulating servant API types. The
functionality in this module is for advanced usage.
The code samples in this module use the following type synonym:
type SampleAPI = "hello" :> Get '[JSON] Int
:<|> "bye" :> Capture "name" String :> Post '[JSON, PlainText] BoolThe doctests in this module are run with following preamble:
:set -XPolyKinds:set -XGADTs:set -XTypeSynonymInstances -XFlexibleInstancesimport Data.Proxyimport Data.Type.Equalityimport Servant.APIdata OK ctx where OK :: ctx => OK ctxinstance Show (OK ctx) where show _ = "OK"let ok :: ctx => Proxy ctx -> OK ctx; ok _ = OKtype SampleAPI = "hello" :> Get '[JSON] Int :<|> "bye" :> Capture "name" String :> Post '[JSON, PlainText] Booltype FailAPI = Fragment Bool :> Fragment Int :> Get '[JSON] NoContentlet sampleAPI = Proxy :: Proxy SampleAPI
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
You 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
QueryParam that are optional in a URI and do not affect them if they are
omitted.
data CustomThingtype instance IsElem' e (CustomThing :> s) = IsElem e s
Note that IsElem is called, which will mutually recurse back to IsElem'
if it exhausts all other options again.
Once you have written a HasLink instance for CustomThing you are ready to go.
Closed type family, check if endpoint is within api.
Uses IsElem' if it exhausts all other options.
ok (Proxy :: Proxy (IsElem ("hello" :> Get '[JSON] Int) SampleAPI))OK
ok (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)))OK
ok (Proxy :: Proxy (IsElem (Get '[JSON] Int) (ReqBody '[JSON] Bool :> Get '[JSON] Int)))OK
N.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 IsIn).
IsElem e (sa :<|> sb) = Or (IsElem e sa) (IsElem e sb)IsElem (e :> sa) (e :> sb) = IsElem sa sbIsElem sa (Header sym x :> sb) = IsElem sa sbIsElem sa (Header' mods sym x :> sb) = IsElem sa sbIsElem sa (ReqBody y x :> sb) = IsElem sa sbIsElem (CaptureAll z y :> sa) (CaptureAll x y :> sb) = IsElem sa sbIsElem (Capture z y :> sa) (Capture x y :> sb) = IsElem sa sbIsElem sa (QueryParam x y :> sb) = IsElem sa sbIsElem sa (QueryParams x y :> sb) = IsElem sa sbIsElem sa (QueryFlag x :> sb) = IsElem sa sbIsElem sa (Fragment x :> sb) = IsElem sa sbIsElem (Verb m s ct typ) (Verb m s ct' typ) = IsSubList ct ct'IsElem e e = ()IsElem e (NamedRoutes rs) = IsElem e (ToServantApi rs)IsElem e a = IsElem' e aCheck whether sub is a sub-API of api.
ok (Proxy :: Proxy (IsSubAPI SampleAPI (SampleAPI :<|> Get '[JSON] Int)))OK
ok (Proxy :: Proxy (IsSubAPI (SampleAPI :<|> Get '[JSON] Int) SampleAPI))...... Could not ......
This uses IsElem for checking; thus the note there applies here.
Closed type family, check if endpoint is exactly within api.
ok (Proxy :: Proxy (IsIn ("hello" :> Get '[JSON] Int) SampleAPI))OK
Unlike IsElem, this requires an *exact* match.
ok (Proxy :: Proxy (IsIn (Get '[JSON] Int) (Header "h" Bool :> Get '[JSON] Int)))...... Could not ......
Append two type-level lists.
AppendList '[] ys = ysAppendList (x ': xs) ys = x ': AppendList xs ysCheck 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......
If both a or b produce an empty constraint, produce an empty constraint.
And () () = ()FragmentUnique (sa :<|> sb) = And (FragmentUnique sa) (FragmentUnique sb)FragmentUnique (Fragment a :> sa) = FragmentNotIn sa (Fragment a :> sa)FragmentUnique (x :> sa) = FragmentUnique saFragmentUnique (Fragment a) = ()FragmentUnique x = ()If there is more than one fragment in an API endpoint, a compile-time error is raised.
type FailAPI = Fragment Bool :> Fragment Int :> Get '[JSON] NoContentinstance AtMostOneFragment FailAPI......Only one Fragment allowed per endpoint in api......
AtMostOneFragment (Fragment a)Defined in servant-0.20.2 · Servant.API.TypeLevelAtMostOneFragment (UVerb m cts as)Defined in servant-0.20.2 · Servant.API.TypeLevelAtMostOneFragment (Verb m s ct typ)Defined in servant-0.20.2 · Servant.API.TypeLevel