HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Moduleservant-0.20.2Haskell2010

Servant.Links

Type safe generation of internal links.

Given an API with a few endpoints:

Example10 expressions
:set -XDataKinds -XTypeFamilies -XTypeOperators -XPolyKindsimport Servant.APIimport Servant.Linksimport Web.HttpApiData (toUrlPiece)import Data.Proxytype Hello = "hello" :> Get '[JSON] Inttype Bye   = "bye"   :> QueryParam "name" String :> Delete '[JSON] NoContenttype API   = Hello :<|> Byelet api = Proxy :: Proxy API

It is possible to generate links that are guaranteed to be within API with safeLink. The first argument to safeLink is a type representing the API you would like to restrict links to. The second argument is the destination endpoint you would like the link to point to, this will need to end with a verb like GET or POST. Further arguments may be required depending on the type of the endpoint. If everything lines up you will get a Link out the other end.

You may omit QueryParams and the like should you not want to provide them, but types which form part of the URL path like Capture must be included. The reason you may want to omit QueryParams is that safeLink is a bit magical: if parameters are included that could take input it will return a function that accepts that input and generates a link. This is best shown with an example. Here, a link is generated with no parameters:

Example2 expressions
let hello = Proxy :: Proxy ("hello" :> Get '[JSON] Int)toUrlPiece (safeLink api hello :: Link)"hello"

If the API has an endpoint with parameters then we can generate links with or without those:

Example2 expressions
let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete '[JSON] NoContent)toUrlPiece $ safeLink api with (Just "Hubert")"bye?name=Hubert"
Example2 expressions
let without = Proxy :: Proxy ("bye" :> Delete '[JSON] NoContent)toUrlPiece $ safeLink api without"bye"

If you would like to create a helper for generating links only within that API, you can partially apply safeLink if you specify a correct type signature like so:

Example6 expressions
:set -XConstraintKinds:{let apiLink :: (IsElem endpoint API, HasLink endpoint)            => Proxy endpoint -> MkLink endpoint Link    apiLink = safeLink api:}

safeLink' allows you to specialise the output:

Example1 expression
safeLink' toUrlPiece api without"bye"
Example5 expressions
:{let apiTextLink :: (IsElem endpoint API, HasLink endpoint)                 => Proxy endpoint -> MkLink endpoint Text    apiTextLink = safeLink' toUrlPiece api:}
Example1 expression
apiTextLink without"bye"

Attempting to construct a link to an endpoint that does not exist in api will result in a type error like this:

Example2 expressions
let bad_link = Proxy :: Proxy ("hello" :> Delete '[JSON] NoContent)safeLink api bad_link......Could not ......

This error is essentially saying that the type family couldn't find bad_link under api after trying the open (but empty) type family IsElem' as a last resort.

  • 5 types
  • 1 class
  • 13 values
  • Packageservant-0.20.2
  • Exports19
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceLinks.hs

Building and using safe links

5 declarations

Note that URI is from the Network.URI module in the network-uri package.

datadata URI
#

Represents a general universal resource identifier using its component parts.

For example, for the URI

  foo://anonymous@www.haskell.org:42/ghc?query#frag

the components are:

Constructors

Instances12Eq, Data, Ord, Show, Generic, NFData, …

Generics

5 declarations

Adding custom types

5 declarations
valuelinkURI :: Link -> URI
#

Transform Link into URI.

Example2 expressions
type API = "something" :> Get '[JSON] IntlinkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)something
Example2 expressions
type API = "sum" :> QueryParams "x" Int :> Get '[JSON] IntlinkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x[]=1&x[]=2&x[]=3
Example2 expressions
type API = "foo/bar" :> Get '[JSON] IntlinkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)foo%2Fbar
Example3 expressions
type SomeRoute = "abc" :> Capture "email" String :> Put '[JSON] ()let someRoute = Proxy :: Proxy SomeRoutesafeLink someRoute someRoute "test@example.com"Link {_segments = ["abc","test%40example.com"], _queryParams = [], _fragment = Nothing}
Example1 expression
linkURI $ safeLink someRoute someRoute "test@example.com"abc/test%40example.com

Configurable linkURI.

Example2 expressions
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[]=3
Example1 expression
linkURI' LinkArrayElementPlain $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x=1&x=2&x=3

How to encode array query elements.

Constructors

Instances5Bounded, Enum, Eq, Ord, Show

Link accessors