packages feed

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`.