| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Servant.Links
Description
Type safe generation of internal links.
Given an API with a few endpoints:
>>>:set -XDataKinds -XTypeFamilies -XTypeOperators>>>import Servant.API>>>import Servant.Links>>>import Web.HttpApiData (toUrlPiece)>>>import Data.Proxy>>>>>>type Hello = "hello" :> Get '[JSON] Int>>>type Bye = "bye" :> QueryParam "name" String :> Delete '[JSON] NoContent>>>type API = Hello :<|> Bye>>>let 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:
>>>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:
>>>let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete '[JSON] NoContent)>>>toUrlPiece $ safeLink api with (Just "Hubert")"bye?name=Hubert"
>>>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:
>>>:set -XConstraintKinds>>>:{>>>let apiLink :: (IsElem endpoint API, HasLink endpoint)>>>=> Proxy endpoint -> MkLink endpoint Link>>>apiLink = safeLink api>>>:}
safeLink' allows you to specialise the output:
>>>safeLink' toUrlPiece api without"bye"
>>>:{>>>let apiTextLink :: (IsElem endpoint API, HasLink endpoint)>>>=> Proxy endpoint -> MkLink endpoint Text>>>apiTextLink = safeLink' toUrlPiece api>>>:}
>>>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:
>>>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.
Since: 0.14.1
Synopsis
- module Servant.API.TypeLevel
- safeLink :: forall endpoint api. (IsElem endpoint api, HasLink endpoint) => Proxy api -> Proxy endpoint -> MkLink endpoint Link
- safeLink' :: forall endpoint api a. (IsElem endpoint api, HasLink endpoint) => (Link -> a) -> Proxy api -> Proxy endpoint -> MkLink endpoint a
- allLinks :: forall api. HasLink api => Proxy api -> MkLink api Link
- allLinks' :: forall api a. HasLink api => (Link -> a) -> Proxy api -> MkLink api a
- data URI = URI {
- uriScheme :: String
- uriAuthority :: Maybe URIAuth
- uriPath :: String
- uriQuery :: String
- uriFragment :: String
- data AsLink (a :: *)
- fieldLink :: (IsElem endpoint (ToServantApi routes), HasLink endpoint, GenericServant routes AsApi) => (routes AsApi -> endpoint) -> MkLink endpoint Link
- fieldLink' :: forall routes endpoint a. (IsElem endpoint (ToServantApi routes), HasLink endpoint, GenericServant routes AsApi) => (Link -> a) -> (routes AsApi -> endpoint) -> MkLink endpoint a
- allFieldLinks :: (HasLink (ToServantApi routes), GenericServant routes (AsLink Link), ToServant routes (AsLink Link) ~ MkLink (ToServantApi routes) Link) => routes (AsLink Link)
- allFieldLinks' :: forall routes a. (HasLink (ToServantApi routes), GenericServant routes (AsLink a), ToServant routes (AsLink a) ~ MkLink (ToServantApi routes) a) => (Link -> a) -> routes (AsLink a)
- class HasLink endpoint where
- data Link
- linkURI :: Link -> URI
- linkURI' :: LinkArrayElementStyle -> Link -> URI
- data LinkArrayElementStyle
- data Param
- = SingleParam String Text
- | ArrayElemParam String Text
- | FlagParam String
- linkSegments :: Link -> [String]
- linkQueryParams :: Link -> [Param]
- linkFragment :: Link -> Fragment'
Documentation
module Servant.API.TypeLevel
Building and using safe links
Note that URI is from the Network.URI module in the network-uri package.
Arguments
| :: forall endpoint api. (IsElem endpoint api, HasLink endpoint) | |
| => Proxy api | The whole API that this endpoint is a part of |
| -> Proxy endpoint | The API endpoint you would like to point to |
| -> MkLink endpoint Link |
Create a valid (by construction) relative URI with query params.
This function will only typecheck if endpoint is part of the API api
Arguments
| :: forall endpoint api a. (IsElem endpoint api, HasLink endpoint) | |
| => (Link -> a) | |
| -> Proxy api | The whole API that this endpoint is a part of |
| -> Proxy endpoint | The API endpoint you would like to point to |
| -> MkLink endpoint a |
More general safeLink.
allLinks :: forall api. HasLink api => Proxy api -> MkLink api Link Source #
Create 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] Double>>>let fooLink :<|> barLink = allLinks (Proxy :: Proxy API)>>>:t fooLinkfooLink :: Text -> Link>>>:t barLinkbarLink :: Int -> Link
Note: 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 :: * = Char -> (Int -> Link) :<|> (Double -> Link)
Constructors
| URI | |
Fields
| |
Instances
| Data URI | |
Defined in Network.URI Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> URI -> c URI gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c URI dataTypeOf :: URI -> DataType dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c URI) dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c URI) gmapT :: (forall b. Data b => b -> b) -> URI -> URI gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> URI -> r gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> URI -> r gmapQ :: (forall d. Data d => d -> u) -> URI -> [u] gmapQi :: Int -> (forall d. Data d => d -> u) -> URI -> u gmapM :: Monad m => (forall d. Data d => d -> m d) -> URI -> m URI gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> URI -> m URI gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> URI -> m URI | |
| Generic URI | |
| Show URI | |
| NFData URI | |
Defined in Network.URI | |
| Eq URI | |
| Ord URI | |
| Lift URI | |
| type Rep URI | |
Defined in Network.URI type Rep URI = D1 ('MetaData "URI" "Network.URI" "network-uri-2.6.4.2-GrvYkDrbqYd92ahEuUJaxX" 'False) (C1 ('MetaCons "URI" 'PrefixI 'True) ((S1 ('MetaSel ('Just "uriScheme") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String) :*: S1 ('MetaSel ('Just "uriAuthority") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe URIAuth))) :*: (S1 ('MetaSel ('Just "uriPath") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String) :*: (S1 ('MetaSel ('Just "uriQuery") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String) :*: S1 ('MetaSel ('Just "uriFragment") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String))))) | |
Generics
A type that specifies that an API record contains a set of links.
Since: 0.14.1
fieldLink :: (IsElem endpoint (ToServantApi routes), HasLink endpoint, GenericServant routes AsApi) => (routes AsApi -> endpoint) -> MkLink endpoint Link Source #
Given 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 (Generic)
getLink :: Int -> Link
getLink = fieldLink _get
Since: 0.14.1
fieldLink' :: forall routes endpoint a. (IsElem endpoint (ToServantApi routes), HasLink endpoint, GenericServant routes AsApi) => (Link -> a) -> (routes AsApi -> endpoint) -> MkLink endpoint a Source #
More general version of fieldLink
Since: 0.14.1
allFieldLinks :: (HasLink (ToServantApi routes), GenericServant routes (AsLink Link), ToServant routes (AsLink Link) ~ MkLink (ToServantApi routes) Link) => routes (AsLink Link) Source #
Get all links as a record.
Since: 0.14.1
allFieldLinks' :: forall routes a. (HasLink (ToServantApi routes), GenericServant routes (AsLink a), ToServant routes (AsLink a) ~ MkLink (ToServantApi routes) a) => (Link -> a) -> routes (AsLink a) Source #
More general version of allFieldLinks.
Since: 0.14.1
Adding custom types
class HasLink endpoint where Source #
Construct a toLink for an endpoint.
Methods
Instances
| HasLink EmptyAPI Source # | |
| HasLink Raw Source # | |
| HasLink RawM Source # | |
| (TypeError (NoInstanceFor (HasLink api)) :: Constraint) => HasLink (api :: k) Source # | |
| (HasLink (ToServantApi routes), forall a. GLink routes a, ErrorIfNoGeneric routes) => HasLink (NamedRoutes routes :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (NamedRoutes routes) a Source # Methods toLink :: (Link -> a) -> Proxy (NamedRoutes routes) -> Link -> MkLink (NamedRoutes routes) a Source # | |
| (HasLink a, HasLink b) => HasLink (a :<|> b :: Type) Source # | |
| HasLink (NoContentVerb m :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (NoContentVerb m) a Source # Methods toLink :: (Link -> a) -> Proxy (NoContentVerb m) -> Link -> MkLink (NoContentVerb m) a Source # | |
| (TypeError (PartialApplication (HasLink :: Type -> Constraint) arr) :: Constraint) => HasLink (arr :> sub :: Type) Source # | |
| (KnownSymbol sym, HasLink sub) => HasLink (sym :> sub :: Type) Source # | |
| HasLink sub => HasLink (HttpVersion :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (HttpVersion :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (HttpVersion :> sub) -> Link -> MkLink (HttpVersion :> sub) a Source # | |
| HasLink sub => HasLink (BasicAuth realm a :> sub :: Type) Source # | |
| (ToHttpApiData v, HasLink sub) => HasLink (Capture' mods sym v :> sub :: Type) Source # | |
| (ToHttpApiData v, HasLink sub) => HasLink (CaptureAll sym v :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (CaptureAll sym v :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (CaptureAll sym v :> sub) -> Link -> MkLink (CaptureAll sym v :> sub) a Source # | |
| HasLink sub => HasLink (Description s :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (Description s :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (Description s :> sub) -> Link -> MkLink (Description s :> sub) a Source # | |
| HasLink sub => HasLink (Summary s :> sub :: Type) Source # | |
| HasLink sub => HasLink (AuthProtect tag :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (AuthProtect tag :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (AuthProtect tag :> sub) -> Link -> MkLink (AuthProtect tag :> sub) a Source # | |
| (HasLink sub, ToHttpApiData v) => HasLink (Fragment v :> sub :: Type) Source # | |
| HasLink sub => HasLink (Header' mods sym a :> sub :: Type) Source # | |
| HasLink sub => HasLink (IsSecure :> sub :: Type) Source # | |
| (KnownSymbol sym, HasLink sub) => HasLink (QueryFlag sym :> sub :: Type) Source # | |
| (KnownSymbol sym, ToHttpApiData v, HasLink sub, SBoolI (FoldRequired mods)) => HasLink (QueryParam' mods sym v :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (QueryParam' mods sym v :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (QueryParam' mods sym v :> sub) -> Link -> MkLink (QueryParam' mods sym v :> sub) a Source # | |
| (KnownSymbol sym, ToHttpApiData v, HasLink sub) => HasLink (QueryParams sym v :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (QueryParams sym v :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (QueryParams sym v :> sub) -> Link -> MkLink (QueryParams sym v :> sub) a Source # | |
| HasLink sub => HasLink (RemoteHost :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (RemoteHost :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (RemoteHost :> sub) -> Link -> MkLink (RemoteHost :> sub) a Source # | |
| HasLink sub => HasLink (ReqBody' mods ct a :> sub :: Type) Source # | |
| HasLink sub => HasLink (StreamBody' mods framing ct a :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (StreamBody' mods framing ct a :> sub) a Source # Methods toLink :: (Link -> a0) -> Proxy (StreamBody' mods framing ct a :> sub) -> Link -> MkLink (StreamBody' mods framing ct a :> sub) a0 Source # | |
| HasLink sub => HasLink (WithResource res :> sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (WithResource res :> sub) a Source # Methods toLink :: (Link -> a) -> Proxy (WithResource res :> sub) -> Link -> MkLink (WithResource res :> sub) a Source # | |
| HasLink sub => HasLink (Vault :> sub :: Type) Source # | |
| (TypeError (NoInstanceForSub (HasLink :: Type -> Constraint) ty) :: Constraint) => HasLink (ty :> sub :: Type) Source # | |
| HasLink (UVerb m ct a :: Type) Source # | |
| HasLink sub => HasLink (WithNamedContext name context sub :: Type) Source # | |
Defined in Servant.Links Associated Types type MkLink (WithNamedContext name context sub) a Source # Methods toLink :: (Link -> a) -> Proxy (WithNamedContext name context sub) -> Link -> MkLink (WithNamedContext name context sub) a Source # | |
| HasLink (Verb m s ct a :: Type) Source # | |
| HasLink (Stream m status fr ct a :: Type) Source # | |
A safe link datatype.
The only way of constructing a Link is using safeLink, which means any
Link is guaranteed to be part of the mentioned API.
Instances
| Show Link Source # | |
| ToHttpApiData Link Source # | |
Defined in Servant.Links Methods toUrlPiece :: Link -> Text # toEncodedUrlPiece :: Link -> Builder # toHeader :: Link -> ByteString # toQueryParam :: Link -> Text # toEncodedQueryParam :: Link -> Builder # | |
linkURI :: Link -> URI Source #
>>>type API = "something" :> Get '[JSON] Int>>>linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)something
>>>type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int>>>linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x[]=1&x[]=2&x[]=3
>>>type API = "foo/bar" :> Get '[JSON] Int>>>linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)foo%2Fbar
>>>type SomeRoute = "abc" :> 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}
>>>linkURI $ safeLink someRoute someRoute "test@example.com"abc/test%40example.com
linkURI' :: LinkArrayElementStyle -> Link -> URI Source #
Configurable linkURI.
>>>type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int>>>linkURI' LinkArrayElementBracket $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x[]=1&x[]=2&x[]=3
>>>linkURI' LinkArrayElementPlain $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]sum?x=1&x=2&x=3
data LinkArrayElementStyle Source #
How to encode array query elements.
Constructors
| LinkArrayElementBracket | foo[]=1&foo[]=2 |
| LinkArrayElementPlain | foo=1&foo=2 |
Instances
Link accessors
Query parameter.
Constructors
| SingleParam String Text | |
| ArrayElemParam String Text | |
| FlagParam String |
linkSegments :: Link -> [String] Source #
linkQueryParams :: Link -> [Param] Source #
linkFragment :: Link -> Fragment' Source #