Search code examples
haskellservant

Making client bindings in Haskell servant - ConnectionError


I am trying to make client bindings for the News api provided by newsapi.org using the Haskell servant library. I have created the following endpoint:

type NewsAPI = "top-headlines" :> QueryParam "country" String :> QueryParam "apiKey" String :> Get '[JSON] TopHeadlines

And attempted to call it as follows:

topheadlines :: Maybe String -> Maybe String -> ClientM TopHeadlines

api :: Proxy NewsAPI
api = Proxy

topheadlines = client api

query = topheadlines (Just "us") (Just "<api key>")

run3 :: IO ()
run3 = do
  manager' <- newManager defaultManagerSettings
  users <- runClientM query (mkClientEnv manager' (BaseUrl Https "newsapi.org/v2" 443 ""))
  print users

I keep getting a connection error that I don't entirely understand how to reason about:

Left (ConnectionError "HttpExceptionRequest Request {\n host = \"newsapi.org/v2\"\n port = 443\n secure = True\n requestHeaders = [(\"Accept\",\"application/json;charset=utf-8,application/json\")]\n path = \"/top-headlines\"\n queryString = \"?country=us&api_key=90a38fab85c440fa88521e0789248f83\"\n method = \"GET\"\n proxy = Nothing\n rawBody = False\n redirectCount = 10\n responseTimeout = ResponseTimeoutDefault\n requestVersion = HTTP/1.1\n}\n TlsNotSupported")

Not sure why there is no connection. Another set of client bindings I have is working fine.


Solution

  • It is a combination of two things:

    1. api_key should be apiKey
    2. Rely on parseBaseUrl

      burl  <- parseBaseUrl "http://newsapi.org/v2"
      

    This worked for me in the sample project I set up.

    {-# LANGUAGE DataKinds     #-}
    {-# LANGUAGE TypeOperators #-}
    
    module Main where
    
    import           Control.Monad.Free
    import           Servant.Client.Free
    
    import qualified Network.HTTP.Client                as HTTP
    import qualified Servant.Client.Internal.HttpClient as I
    
    import           Network.Wai.Handler.Warp           (run)
    import           Servant
    import           System.Environment                 (getArgs)
    
    type NewsAPI = "top-headlines" :> QueryParam "country" String :> QueryParam "apiKey" String :> Get '[JSON] String
    
    
    topheadlines :: Maybe String -> Maybe String -> Free ClientF String
    topheadlines = client api
    
    
    api :: Proxy NewsAPI
    api = Proxy
    
    
    main :: IO ()
    main = do
      test
    
    
    test :: IO ()
    test = case topheadlines (Just "us") (Just "API_KEY") of
        Pure n ->
            putStrLn $ "ERROR: got pure result: " ++ show n
    
        Free (Throw err) ->
            putStrLn $ "ERROR: got error right away: " ++ show err
    
        Free (StreamingRequest _req _k) ->
            putStrLn $ "ERROR: need to do streaming request" 
        Free (RunRequest req k) -> do
            burl  <- parseBaseUrl "http://newsapi.org/v2"
            mgr   <- HTTP.newManager HTTP.defaultManagerSettings
    
            let req' = I.requestToClientRequest burl req
            putStrLn $ "Making request: " ++ show req'
    
            res' <- HTTP.httpLbs req' mgr
            putStrLn $ "Got response: " ++ show res'