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