request-0.4.1.0: README.md
# request

HTTP client for haskell, inpired by [requests](https://requests.readthedocs.io/) and [http-dispatch](https://github.com/owainlewis/http-dispatch).
[](https://deepwiki.com/aisk/request)
## Installation
This pacakge is published on [hackage](http://hackage.haskell.org/package/request) with the same name `request`, you can install it with cabal or stack or nix as any other hackage packages.
## Usage
This library supports modern Haskell record dot syntax. First, enable these language extensions:
```haskell
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
```
Then you can use the library like this:
```haskell
import Network.HTTP.Request
import qualified Data.ByteString as BS
-- Using shortcuts
resp <- get "https://api.leancloud.cn/1.1/date"
print resp.status -- 200
-- Or construct a Request manually
let req = Request { method = GET, url = "https://api.leancloud.cn/1.1/date", headers = [], body = () }
-- Response with ByteString body
responseBS <- send req :: IO (Response BS.ByteString)
print responseBS.status -- 200
print responseBS.body -- ByteString response
-- Response with String body
responseStr <- send req :: IO (Response String)
print responseStr.body -- String response
```
## Core API
Request's API has three core concepts: `Request` record type, `Response` record type, `send` function.
### Request
`Request a` is all about the information you will send to the target URL. The type parameter `a` is the body type, it can be any type that implements `ToRequestBody`. When `send` is called, the body is automatically serialized and the appropriate `Content-Type` header is inferred, unless you set it manually.
```haskell
data Request a = Request
{ method :: Method
, url :: String
, headers :: Headers
, body :: a
} deriving (Show)
```
Built-in `ToRequestBody` instances and their inferred `Content-Type`:
- `()` → empty body, no Content-Type
- `ByteString` / lazy `ByteString` / `Text` / `String` → `text/plain; charset=utf-8`
- Any type with a `ToJSON` instance → auto JSON encoding + `application/json`
The `Content-Type` is automatically inferred from the body type. You can override it by setting the header manually:
```haskell
-- Content-Type is auto-inferred from body type
send $ Request POST url [] body
-- Or override Content-Type manually
send $ Request POST url [("Content-Type", "text/xml")] xmlBytes
```
### Response
`Response` is what you got from the server URL.
```haskell
data Response a = Response
{ status :: Int
, headers :: Headers
, body :: a
} deriving (Show)
```
The response body type `a` can be any type that implements the `FromResponseBody` constraint, allowing flexible handling of response data. Built-in supported types include `String`, `ByteString`, `Text`, and any type with a `FromJSON` instance.
### send
Once you have constructed your own `Request` record, you can call the `send` function to send it to the server. It automatically serializes the body and infers the `Content-Type` header. The `send` function's type is:
```haskell
send :: (ToRequestBody a, FromResponseBody b) => Request a -> IO (Response b)
```
## JSON Support
### JSON Response
For any type with a `FromJSON` instance, the response body will be automatically decoded:
```haskell
{-# LANGUAGE DeriveGeneric #-}
import Network.HTTP.Request
import Data.Aeson (FromJSON)
import GHC.Generics (Generic)
data Date = Date
{ __type :: String
, iso :: String
} deriving (Show, Generic)
instance FromJSON Date
main :: IO ()
main = do
response <- get "https://api.leancloud.cn/1.1/date" :: IO (Response Date)
print response.status -- 200
print response.body -- Date { __type = "Date", iso = "..." }
```
If JSON decoding fails, an `AesonException` will be thrown, which can be caught with `Control.Exception.catch` or `try`.
### JSON Request Body
The `post`, `put`, and `patch` shortcuts accept any type that implements `ToRequestBody`. For types with a `ToJSON` instance, the body is automatically JSON-encoded and `Content-Type: application/json` is set:
```haskell
{-# LANGUAGE DeriveGeneric #-}
import Network.HTTP.Request
import Data.Aeson (ToJSON)
import GHC.Generics (Generic)
data User = User { name :: String } deriving (Show, Generic)
instance ToJSON User
main :: IO ()
main = do
response <- post "https://httpbin.org/post" (User "Alice") :: IO (Response String)
print response.status -- 200
```
## Shortcuts
As you expected, there are some shortcuts for the most used scenarios.
```haskell
get :: (FromResponseBody a) => String -> IO (Response a)
delete :: (FromResponseBody a) => String -> IO (Response a)
post :: (ToRequestBody a, FromResponseBody b) => String -> a -> IO (Response b)
put :: (ToRequestBody a, FromResponseBody b) => String -> a -> IO (Response b)
patch :: (ToRequestBody a, FromResponseBody b) => String -> a -> IO (Response b)
```
These shortcuts' definitions are simple and direct. You are encouraged to add your own if the built-in does not match your use cases, like add custom headers in every request.
## Without Language Extensions
If you prefer not to use the language extensions, you can still use the library with the traditional syntax:
- Create requests using positional arguments: `Request GET "url" [] ()`
- Use prefixed accessor functions: `responseStatus response`, `responseHeaders response`, etc.
```haskell
import Network.HTTP.Request
import qualified Data.ByteString as BS
-- Construct a Request using positional arguments
let req = Request GET "https://api.leancloud.cn/1.1/date" [] ()
-- Send it
res <- send req
-- Access the fields using prefixed accessor functions
print $ responseStatus res
```
## Custom Connection Manager
By default, `send` uses the `http-client` global TLS manager. For most applications this is fine, you get connection pooling for free with no setup. If you want to isolate your library's connection pool from the rest of the program, keep a long-lived manager in a service, or configure proxies and custom TLS settings, create your own manager and pass it to `sendWith`:
```haskell
import Network.HTTP.Request
main :: IO ()
main = do
mgr <- newManager
resp <- sendWith mgr (Request GET "https://api.example.com/things" [] ()) :: IO (Response String)
print resp.status
```
The two new pieces of API:
```haskell
newManager :: IO Manager
sendWith :: (ToRequestBody a, FromResponseBody b) => Manager -> Request a -> IO (Response b)
```
`Manager` is the same type as `Network.HTTP.Client.Manager`, re-exported for convenience. For deeper configuration (`ManagerSettings`, custom proxies, certificate pinning, etc.) import `Network.HTTP.Client` / `Network.HTTP.Client.TLS` directly and build a `Manager` however you need. `sendWith` accepts it as-is.
## Streaming Support
For large responses or real-time data, you can stream the response body instead of buffering it all in memory.
### Raw Byte Chunks
Use `StreamBody BS.ByteString` to receive the response body as a stream of raw byte chunks:
```haskell
import Network.HTTP.Request
import qualified Data.ByteString as BS
main :: IO ()
main = do
let req = Request GET "https://example.com/large-file" [] ()
resp <- send req :: IO (Response (StreamBody BS.ByteString))
print resp.status -- 200
let loop = do
mChunk <- resp.body.readNext
case mChunk of
Nothing -> return () -- stream finished
Just chunk -> do
BS.putStr chunk
loop
loop
resp.body.closeStream
```
### SSE (Server-Sent Events)
Use `StreamBody SseEvent` to automatically parse an SSE stream. Each call to `readNext` returns the next complete event:
```haskell
import Network.HTTP.Request
import qualified Data.Text.IO as T
data SseEvent = SseEvent
{ sseData :: T.Text -- content of the "data:" field
, sseType :: Maybe T.Text -- content of the "event:" field
, sseId :: Maybe T.Text -- content of the "id:" field
}
main :: IO ()
main = do
let req = Request GET "https://example.com/events" [] ()
resp <- send req :: IO (Response (StreamBody SseEvent))
print resp.status -- 200
let loop = do
mEvent <- resp.body.readNext
case mEvent of
Nothing -> return () -- stream finished
Just event -> do
T.putStrLn event.sseData
loop
loop
resp.body.closeStream
```
`StreamBody` has two fields:
- `readNext :: IO (Maybe a)` — reads the next chunk or event; returns `Nothing` when the stream ends
- `closeStream :: IO ()` — closes the underlying connection
## API Documents
See the hackage page: http://hackage.haskell.org/package/request/docs/Network-HTTP-Request.html
## About the Project
Request is © 2020-2026 by [AN Long](https://github.com/aisk).
### License
Request is distributed by a [BSD license](https://github.com/aisk/request/tree/master/LICENSE).