-- | Hreq is a high-level easy to use type-driven HTTP client library inspired by Servant-Client. -- Hreq provides an alternative approach to type-safe construction and interpretation of API -- endpoints for Http client requests. -- Hreq is greatly inspired by Servant Client. -- -- == Examples -- -- Assume we are making requests against an HTTP service providing a JSON user management API. -- -- > {-# LANGUAGE DataKinds #-} -- > {-# LANGUAGE DeriveAnyClass #-} -- > {-# LANGUAGE DeriveGeneric #-} -- > {-# LANGUAGE OverloadedStrings #-} -- > {-# LANGUAGE TypeApplications #-} -- > {-# LANGUAGE TypeOperators #-} -- > -- > import Control.Monad.IO.Class (liftIO) -- > import Data.Aeson (FromJSON, ToJSON) -- > import Data.Text (Text) -- > import GHC.Generics (Generic) -- > import Hreq.Client -- > -- > data User = User -- > { name :: Text -- > , age :: Int -- > } deriving (Show, Generic, FromJSON, ToJSON) -- -- @User@ service API URL -- -- > baseUrl :: BaseUrl -- > baseUrl = HttpUrl "example.com" "user" -- -- ===Simple Get request -- -- Make a Get request obtaining a @User@ by a specified @user-name@ at <http://example.com/user/:userName> -- -- > getUserByName :: RunClient m => Text -> m User -- > getUserByName userName = hreq @(Capture Text :> GetJson User) (userName :. Empty) -- -- The @Capture Text :> GetJson User@ type with in @getUserByName@ is an API endpoint type definition. -- -- The API type definition in this instance demands that a heterogeneous list containing a 'Text' value is supplied to the 'hreq' function. -- -- @userName ':.' 'Empty'@ forms the required heterogeneous list value for the 'hreq' function. -- Finally, the API type states that we will obtain a 'JSON' @User@ response output. -- -- ===Simple Post request -- -- Make a Post request with Json User data for a request body returning a Json User response at <http://example.com/user> -- -- > createUser :: RunClient m => User -> m () -- > createUser user = hreq @(JsonBody User :> EmptyResponse POST) (user :. Empty) -- -- ===Get Request with QueryFlag -- -- Make a Get requesting obtaining all users at API endpoint <http://example.com/user/all?old> -- -- > getAllUsers :: RunClient m => m [User] -- > getAllUsers = hreq @("all" :> QueryFlag "old" :> GetJson [User]) Empty -- -- ===Running api endpoint functions -- -- With in the main function; the API endpoint functions run within the 'Hreq' monad. -- The Hreq monad has an instance of the 'RunClient' class and 'MonadIO' class. -- -- > main :: IO () -- > main = runHreq baseUrl $ do -- > reqUser <- getUserByName "allan" -- > createdUser <- createUser newUser -- > allUsers <- getAllUsers -- > --Delete users with age equal to 20 -- > hreq @(Capture Int :> EmptyResponse DELETE) (20 :. Empty) -- > -- do something with API data -- > liftIO $ print (reqUser, createdUser, allUsers) -- > where -- > newUser :: User -- > newUser = User "allan" 12 -- -- ==More examples -- -- ====Appending a path to the request path -- -- >>> type PathsQuery = "user" :> "allan" :> GetJson User -- -- > pathsExample :: RunClient m => m User -- -- >>> pathsExample = hreq @PathsQuery Empty -- -- ====Adding query params to a request -- -- Any type with a 'ToHttpApiData' class instance can be used as a Param value type. -- -- >>> type SingleParam = Param "name" String :> GetJson User -- -- > singleParamExample :: RunClient m => m Response -- -- >>> singleParamsExample = hreq @SingleParam ("allan" :. Empty) -- -- >>> type MultiParams = Param "name" String :> Param "age" Int :> GetJson User -- -- >>> type MultiParamsList = Params '["name" := String, "age" := Int] :> GetJson User -- -- > -- Note MultiParams and MultiParamsList are the same. -- > -- Resulting URL is of the form http://example.com/api?name="allan"&age=20 -- -- > multiParamsExample :: RunClient m => m User -- -- >>> multiParamsExample = hreq @MultiParams ("allan" :. 20 :. Empty) -- -- ====Adding QueryFlags to a request -- -- >>> type SingleQueryFlag = "user" :> QueryFlag "male" :> GetJson User -- -- > singleQueryFlag :: RunClient m => m User -- -- >>> singleQueryFlag = hreq @SingleQueryFlag Empty -- -- >>> type MultiQueryFlags = "user" :> QueryFlag "male" :> QueryFlag "old" :> GetJson User -- -- >>> type MultiQueryFlagList = "user" :> QueryFlags '["male", "old"] :> GetJson User -- -- > -- Note MultiQueryFlags and MultiQueryFlagsList are the same -- > -- The query flag values are inferred from provided type level strings (symbols) -- > -- Resulting URL is of the form http://example.com/api?male&old -- -- > multiFlagsExample :: RunClient m => m User -- -- >>> multiFlagsExample = hreq @MultiQueryFlagList Empty -- -- ====Adding Captures -- -- Any type with a 'ToHttpApiData' class instance can be used as a 'Capture' value type. -- -- >>> type SingleCapture = Capture UserName :> GetJson User -- -- >>> type MultiCapturesList = "users" :> Captures '[UserName, UserAge] :> GetJson User -- >>> type MultiCaptures = "users" :> Capture UserName :> Capture UserAge :> GetJson User -- -- > -- Resulting URL is of the form http://example.com/users/allan/12 -- > -- Note that MultiCapturesList is equal to MultiCaptures. -- -- > captureExample :: RunClient m => m User -- -- >>> captureExample = hreq @MultiCaptures $ UserName "allan" :. UserAge 12 :. Empty -- -- =====CaptureAll -- -- 'CaptureAll' is useful for a specifying a request composed of multiple URL parameter fragments of the -- same type in a concise manner. -- -- >>> type CaptureAllExample = "users" :> CaptureAll String :> GetJson User -- -- > captureAllExample :: RunClient m => m User -- -- >>> captureAllExample = hreq @CaptureAllExample $ ["allan", "alex", "brian"] :. Empty -- -- ====Adding a Request body -- -- Request bodies are created by the 'ReqBody' type. A request body type is encoded to -- into a byteString basing on the provided media/mime type. -- -- The library nativelysupports some media types such as 'JSON' and 'PlainText' among others. -- -- Example type using JSON as media type, the provided body type should have an Aeson @ToJSON@ instance -- -- >>> type ReqBodyQuery = "users" :> ReqBody User JSON :> GetJson User -- -- The above query can be written as below: -- -- >>> type JsonBodyQuery = "users" :> JsonBody User :> GetJson User -- -- ==== Response type Examples -- -- Response are represented by the @'Verb' (method :: k1) (contents:: [k2])@ type. -- -- @method@ : is a Standard HTTP verb type such as 'GET' or 'POST' -- @contents@ : is a type level list containing expected response from making an http call. -- -- The library provides convenience type synonyms out of the Verb type such as @GetJson@, @PostJson@ etc. -- -- >>> type GetPlainText a = Get '[ResBody PlainText a] -- -- > plainTextResponse :: RunClient m => m String -- -- >>> plainTextResponse = hreq @("user" :> GetPlainText String) Empty -- -- =====Returning multiple values Example -- -- >>> type MultiResultsQuery = Get '[ ResBody JSON User, ResHeaders '[ "key-header" := String ] ] -- -- > multiResults :: RunClient m => m (Hlist '[ User, [Header] ]) -- -- >>> multiResults = hreq @MultiResultsQuery Empty -- module Hreq.Client ( module Hreq.Core.API , module Hreq.Core.Client , module Hreq.Client.Internal.HTTP , module Hreq.Client.Internal.Config ) where import Hreq.Client.Internal.Config (HttpConfig (..), StatusRange (..), createDefConfig) import Hreq.Client.Internal.HTTP (Hreq (..), RunClient (..), runHreq, runHreqWithConfig) import Hreq.Core.API import Hreq.Core.Client -- $setup -- >>> import Hreq.Core.API -- >>> import GHC.Generics -- >>> import Data.Aeson -- >>> import Data.Hlist -- >>> data User = User deriving (Show) -- >>> instance ToJSON User where toJSON = undefined -- >>> instance FromJSON User where parseJSON = undefined -- >>> newtype UserName = UserName { unUserName :: String } deriving (Show, ToHttpApiData) -- >>> newtype UserAge = UserAge { unUserAge :: Int } deriving (Show, ToHttpApiData)