api-maker-0.1.0.0: README.md
# Api Maker: Implementing APIs like never before
Api Maker let's you implement APIs simply by implementing the `class Request` for each possible
request. It is built on top of the [Req](https://hackage.haskell.org/package/req) library.
Lets say we want to implement a `GET` request that returns orders taken in an account. This actually
implements the [GET orders request of
oanda.com](https://developer.oanda.com/rest-live-v20/order-ep/). So we create a data type structure
to hold what we want:
-- | GET Order(s) request
data GetOrder =
GetOrder AccountId OrderConfig
-- | This data type defines the header options that we can send
data OrderConfig = OrderConfig
{ ids :: Maybe [OrderId] -- ^ List of Order IDs to retrieve
, state :: Maybe OrderStateFilter -- ^ The state to filter the requested Orders by [default=PENDING]
, instrument :: Maybe InstrumentName -- ^ The instrument to filter the requested orders by
, orderCount :: Maybe Int -- ^ The maximum number of Orders to return [default=50, maximum=500]
, beforeID :: Maybe OrderId -- ^ The maximum Order ID to return. If not provided the most recent Orders in the Account are returned
} deriving (Show, Eq, Ord, ToJSON, Generic, NFData)
and then we implement the class
instance Request OandaConfig GetOrder where
type Method GetOrder = GET -- It's a GET request
type Body GetOrder = NoReqBody -- We do not send a body
type Response GetOrder = JsonResponse OrderList -- The response is a JSON list of orders
type Output GetOrder = OrderList -- We simply output the list in the `process` function without transformation
method _ GetOrder {} = GET -- GET request again
url cfg (GetOrder accId _) = ".../account/orders" -- The URL
body _ (GetOrder _ req) = NoReqBody -- No Body to send
response _ GetOrder {} = jsonResponse -- The expected response is in JSON format and is parsed using `aeson` with this function.
option _ (GetOrder _ cfg) = -- What header options to send
headerRFC3339DatetimeFormat <> configs
where
configs =
case cfg of
OrderConfig ids state instrument count beforeID ->
"ids" `queryParam` fmap (T.intercalate ";") ids <>
"state" `queryParam` fmap show state <>
"instrument" `queryParam` instrument <>
"count" `queryParam` count <>
"beforeID" `queryParam` beforeID
-- Defines how to process the Response. One might change it here, e.g. filter values that cannot be specified by the request options.
process _ GetOrder {} response = return $ responseBody response
The request `State` monad is then run like this:
runSessReqM cfg $ runRequests $ do
orders <- mkReq $ GetOrder myAccountId (OrderConfig Nothing Nothing (Just "USD/EUR") (Just 100) Nothing)
print orders
This will create the session and run the requests. All `HttpException`s errors are caught and
converted to exceptions. Thus `runSessReqM` returns a `Either HttpException a`. The function `mkReq`
is used to actually make the requests, the data-type `GetOrder` defines what request to make and
uses the corresponding account ID and the configuration provided. The result is a list of Orders,
with type `OrderList`.