@@ -14,33 +14,30 @@
1414* [License](#license)
1515
1616```haskell
17−{-# LANGUAGE OverloadedStrings #-}
18−{-# OPTIONS_GHC -fno-warn-orphans #-}
17+{-# LANGUAGE OverloadedStrings #-}
1918
2019module Main (main) where
2120
22−import Control.Exception (throwIO)
23−import Network.HTTP.Req
21+import Control.Monad.IO.Class
2422import Data.Aeson
25−
26−-- Just make your monad stack an instance of MonadHttp in your application
27−-- and start making requests, enjoy automatic connection sharing.
28−
29−instance MonadHttp IO where
30− handleHttpException = throwIO
23+import Data.Default.Class
24+import Network.HTTP.Req
3125
3226main :: IO ()
33−main = do
27+-- You can either make your monad an instance of 'MonadHttp', or use
28+-- 'runReq' in any IO-enabled monad without defining new instances.
29+main = runReq def $ do
3430 let payload = object
3531 [ "foo" .= (10 :: Int)
3632 , "bar" .= (20 :: Int) ]
37− -- One function, full power and flexibility.
33+ -- One function—full power and flexibility, automatic retrying on timeouts
34+ -- and such, automatic connection sharing.
3835 r <- req POST -- method
3936 (https "httpbin.org" /: "post") -- safe by construction URL
4037 (ReqBodyJson payload) -- use built-in options or add your own
4138 jsonResponse -- specify how to interpret response
4239 mempty -- query params, headers, explicit port number, etc.
43− print (responseBody r :: Value)
40+ liftIO $ print (responseBody r :: Value)
4441```
4542
4643Req is an easy-to-use, type-safe, expandable, high-level HTTP library that
@@ -92,7 +89,6 @@ you the best experience:
9289
9390* [`http-client`](https://hackage.haskell.org/package/http-client)—low level
9491 HTTP client used everywhere in Haskell.
95−
9692* [`http-client-tls`](https://hackage.haskell.org/package/http-client-tls)—TLS
9793 (HTTPS) support for `http-client`.
9894
@@ -112,12 +108,13 @@ I have spent time to write the library because sending HTTP requests is such
112108a common thing and still there is no high-level library for that in Haskell
113109that I could use with pleasure. I'll explain why.
114110
115−First of all there is `http-client` and `http-client-tls`. They just work. I
116−have no issues with the libraries except that they are too low-level for my
117−taste. Indeed, even the docs say that they are low-level and “intended as a
118−base layer for more user-friendly packages”. This is exactly how I use them
119−in Req, as base level. Req is nothing but a different API to `http-client`,
120−so it only works because of the hard work put into `http-client`.
111+First of all, there is `http-client` and `http-client-tls`. They just work.
112+I have no issues with the libraries except that they are too low-level for
113+my taste. Indeed, even the docs say that they are low-level and “intended as
114+a base layer for more user-friendly packages”. This is exactly how I use
115+them in Req, as base level. Req is nothing but a different API to
116+`http-client`, so it only works because of the hard work put into
117+`http-client`.
121118
122119`http-conduit` definitely has its place. For one thing it allows you to
123120stream request and response bodies in constant memory, what other library
@@ -133,29 +130,28 @@ way that their combination is certainly valid. And you parse request from a
133130string without the protection of TH that otherwise saves the day as in
134131Yesod.
135132
136−Then there is Wreq.
137−`wreq`
138−[doesn't see much development lately](https://github.com/bos/wreq/issues/93).
139−`wreq` is by itself a weird library, IMO. You have functions per method—not
140−very good, as there may be new methods, like PATCH which is not new but
141−still missing (well you have `customMethod`, but what is the point of having
142−per-method functions if you have a more general way to use any method? you
143−should be able to just insert methods in the “argument slot” of
144−`customMethod` and end up with a more general solution). Now every method
145−function has a companion that takes `Options` (like you have `get` and
146−`getWith`). Why the duplication? Where is generality and flexibility? This
147−is not all though, because you cannot really use `get` you see in the main
148−module, because you want to have connection sharing. Wreq's author does not
149−take the gift of automatic connection re-use `Manager` from `http-client`
150−provides, he invents the whole new thing of “sessions”. Only inside a
151−session your connections will be shared and re-used. However with the
152−session stuff you have yet another set of per-method functions like `get`
153−and `getWith`—these are different ones, to be used with sessions! Now if you
154−have a multi-threaded app, here is a surprise for you: you can't share
155−connections between threads as connections are shared only inside
156−`withSession` friend and “session will no longer be valid after that
157−function returns”. There are valid uses for sessions, but the point is that
158−they are just too inconvenient for common tasks.
133+Then there is Wreq. `wreq` [doesn't see much development
134+lately](https://github.com/bos/wreq/issues/93). `wreq` is by itself a weird
135+library, IMO. You have functions per method—not very good, as there may be
136+new methods, like PATCH which is not new but still missing (well you have
137+`customMethod`, but what is the point of having per-method functions if you
138+have a more general way to use any method? you should be able to just insert
139+methods in the “argument slot” of `customMethod` and end up with a more
140+general solution). Now every method function has a companion that takes
141+`Options` (like you have `get` and `getWith`). Why the duplication? Where is
142+generality and flexibility? This is not all though, because you cannot
143+really use `get` you see in the main module, because you want to have
144+connection sharing. Wreq's author does not take the gift of automatic
145+connection re-use `Manager` from `http-client` provides, he invents the
146+whole new thing of “sessions”. Only inside a session your connections will
147+be shared and re-used. However with the session stuff you have yet another
148+set of per-method functions like `get` and `getWith`—these are different
149+ones, to be used with sessions! Now if you have a multi-threaded app, here
150+is a surprise for you: you can't share connections between threads as
151+connections are shared only inside `withSession` friend and “session will no
152+longer be valid after that function returns”. There are valid uses for
153+sessions, but the point is that they are just too inconvenient for common
154+tasks.
159155
160156It's funny that one client I worked for had to have his own little wrapper
161157around `http-client` just because he could not possibly use `wreq` and
@@ -164,9 +160,10 @@ extracted from a talk with a Haskell developer who works for that client. I
164160thought to myself “something is wrong with HTTP client libraries in Haskell
165161if they had to make a wrapper”.
166162
167−What else? I used `servant-client` a couple of times but amount of
168−boilerplate is too high. If you have several query parameters, and you use
169−just one of them, good luck passing lots of `Nothing`s.
163+What else? I used `servant-client` a couple of times but the amount of
164+boilerplate it requires is frightening. If you have several query
165+parameters, and you use just one of them, good luck passing lots of
166+`Nothing`s.
170167
171168## Unsolved problems
172169
@@ -177,11 +174,11 @@ running the action and consuming body in its entirety before the request in
177174made. In Wreq the author chose to just use `error` when body is not a
178175(strict or lazy) `ByteString`. Maybe it's OK for Wreq, but I don't consider
179176this a proper solution for Req as we support full variety of body options.
180−For example what if I want to upload 1 Gb file to S3? I want to stream it in
181−constant memory but at the same time I need to calculate its hash before I
182−start streaming. One solution to the problem seems to be in taking the hash
183−explicitly (as an argument of the hypothetical `awsAuth`) and making it a
184−responsibility of the user to calculate the hash correctly. I don't like
177+For example, what if I want to upload 1 Gb file to S3? I want to stream it
178+in constant memory but at the same time I need to calculate its hash before
179+I start streaming. One solution to the problem seems to be in taking the
180+hash explicitly (as an argument of the hypothetical `awsAuth`) and making it
181+a responsibility of the user to calculate the hash correctly. I don't like
185182this because it's not user-friendly. So the question stays open, for now
186183there is no AWS signing functionality provided out-of-the-box. The best
187184solution for talking to AWS is the `amazonka` package so far.