{"id":46765969,"url":"https://github.com/aisk/haskell-request","last_synced_at":"2026-03-09T22:35:11.413Z","repository":{"id":56876560,"uuid":"310598180","full_name":"aisk/haskell-request","owner":"aisk","description":"Simple and ergonomic HTTP client for Haskell","archived":false,"fork":false,"pushed_at":"2026-03-01T10:48:18.000Z","size":53,"stargazers_count":17,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-03-01T14:46:28.691Z","etag":null,"topics":["haskell","http","http-client","request","requests"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aisk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-11-06T12:59:33.000Z","updated_at":"2026-03-01T10:48:22.000Z","dependencies_parsed_at":"2025-05-01T10:56:03.047Z","dependency_job_id":"75a520fa-ada1-49f3-8af6-b848e7e4f612","html_url":"https://github.com/aisk/haskell-request","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/aisk/haskell-request","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aisk%2Fhaskell-request","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aisk%2Fhaskell-request/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aisk%2Fhaskell-request/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aisk%2Fhaskell-request/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aisk","download_url":"https://codeload.github.com/aisk/haskell-request/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aisk%2Fhaskell-request/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30314633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T20:05:46.299Z","status":"ssl_error","status_checked_at":"2026-03-09T19:57:04.425Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["haskell","http","http-client","request","requests"],"created_at":"2026-03-09T22:35:10.818Z","updated_at":"2026-03-09T22:35:11.404Z","avatar_url":"https://github.com/aisk.png","language":"Haskell","readme":"# request\n\n![](https://miro.medium.com/max/1200/1*5KglaZoNp4fNpNHUao5u5w.jpeg)\n\nHTTP client for haskell, inpired by [requests](https://requests.readthedocs.io/) and [http-dispatch](https://github.com/owainlewis/http-dispatch).\n\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/aisk/request)\n\n## Installation\n\nThis 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.\n\n## Usage\n\nThis library supports modern Haskell record dot syntax. First, enable these language extensions:\n\n```haskell\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE OverloadedRecordDot #-}\n```\n\nThen you can use the library like this:\n\n```haskell\nimport Network.HTTP.Request\nimport qualified Data.ByteString as BS\n\n-- Using shortcuts\nresp \u003c- get \"https://api.leancloud.cn/1.1/date\"\nprint resp.status        -- 200\n\n-- Or construct a Request manually\nlet req = Request { method = GET, url = \"https://api.leancloud.cn/1.1/date\", headers = [], body = (Nothing :: Maybe BS.ByteString) }\n\n-- Response with ByteString body\nresponseBS \u003c- send req :: IO (Response BS.ByteString)\nprint responseBS.status        -- 200\nprint responseBS.body          -- ByteString response\n\n-- Response with String body\nresponseStr \u003c- send req :: IO (Response String)\nprint responseStr.body         -- String response\n```\n\n## Core API\n\nRequest's API has three core concepts: `Request` record type, `Response` record type, `send` function.\n\n### Request\n\n`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.\n\n```haskell\ndata Request a = Request\n  { method  :: Method\n  , url     :: String\n  , headers :: Headers\n  , body    :: Maybe a\n  } deriving (Show)\n```\n\nBuilt-in `ToRequestBody` instances and their inferred `Content-Type`:\n\n- `ByteString` / lazy `ByteString` / `Text` / `String` → `text/plain; charset=utf-8`\n- Any type with a `ToJSON` instance → auto JSON encoding + `application/json`\n\nThe `Content-Type` is automatically inferred from the body type. You can override it by setting the header manually:\n\n```haskell\n-- Content-Type is auto-inferred from body type\nsend $ Request POST url [] (Just body)\n\n-- Or override Content-Type manually\nsend $ Request POST url [(\"Content-Type\", \"text/xml\")] (Just xmlBytes)\n```\n\n### Response\n\n`Response` is what you got from the server URL.\n\n```haskell\ndata Response a = Response\n  { status  :: Int\n  , headers :: Headers\n  , body    :: a\n  } deriving (Show)\n```\n\nThe 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.\n\n### send\n\nOnce 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:\n\n```haskell\nsend :: (ToRequestBody a, FromResponseBody b) =\u003e Request a -\u003e IO (Response b)\n```\n\n## JSON Support\n\n### JSON Response\n\nFor any type with a `FromJSON` instance, the response body will be automatically decoded:\n\n```haskell\n{-# LANGUAGE DeriveGeneric #-}\n\nimport Network.HTTP.Request\nimport Data.Aeson (FromJSON)\nimport GHC.Generics (Generic)\n\ndata Date = Date\n  { __type :: String\n  , iso :: String\n  } deriving (Show, Generic)\n\ninstance FromJSON Date\n\nmain :: IO ()\nmain = do\n  response \u003c- get \"https://api.leancloud.cn/1.1/date\" :: IO (Response Date)\n  print response.status  -- 200\n  print response.body    -- Date { __type = \"Date\", iso = \"...\" }\n```\n\nIf JSON decoding fails, an `AesonException` will be thrown, which can be caught with `Control.Exception.catch` or `try`.\n\n### JSON Request Body\n\nThe `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:\n\n```haskell\n{-# LANGUAGE DeriveGeneric #-}\n\nimport Network.HTTP.Request\nimport Data.Aeson (ToJSON)\nimport GHC.Generics (Generic)\n\ndata User = User { name :: String } deriving (Show, Generic)\n\ninstance ToJSON User\n\nmain :: IO ()\nmain = do\n  response \u003c- post \"https://httpbin.org/post\" (User \"Alice\") :: IO (Response String)\n  print response.status  -- 200\n```\n\n## Shortcuts\n\nAs you expected, there are some shortcuts for the most used scenarios.\n\n```haskell\nget    :: (FromResponseBody a) =\u003e String -\u003e IO (Response a)\ndelete :: (FromResponseBody a) =\u003e String -\u003e IO (Response a)\npost   :: (ToRequestBody a, FromResponseBody b) =\u003e String -\u003e a -\u003e IO (Response b)\nput    :: (ToRequestBody a, FromResponseBody b) =\u003e String -\u003e a -\u003e IO (Response b)\npatch  :: (ToRequestBody a, FromResponseBody b) =\u003e String -\u003e a -\u003e IO (Response b)\n```\n\nThese 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.\n\n## Without Language Extensions\n\nIf you prefer not to use the language extensions, you can still use the library with the traditional syntax:\n\n- Create requests using positional arguments: `Request GET \"url\" [] (Nothing :: Maybe BS.ByteString)`\n- Use prefixed accessor functions: `responseStatus response`, `responseHeaders response`, etc.\n\n```haskell\nimport Network.HTTP.Request\nimport qualified Data.ByteString as BS\n\n-- Construct a Request using positional arguments\nlet req = Request GET \"https://api.leancloud.cn/1.1/date\" [] (Nothing :: Maybe BS.ByteString)\n-- Send it\nres \u003c- send req\n-- Access the fields using prefixed accessor functions\nprint $ responseStatus res\n```\n\n## Streaming Support\n\nFor large responses or real-time data, you can stream the response body instead of buffering it all in memory.\n\n### Raw Byte Chunks\n\nUse `StreamBody BS.ByteString` to receive the response body as a stream of raw byte chunks:\n\n```haskell\nimport Network.HTTP.Request\nimport qualified Data.ByteString as BS\n\nmain :: IO ()\nmain = do\n  let req = Request GET \"https://example.com/large-file\" [] (Nothing :: Maybe BS.ByteString)\n  resp \u003c- send req :: IO (Response (StreamBody BS.ByteString))\n  print resp.status  -- 200\n\n  let loop = do\n        mChunk \u003c- resp.body.readNext\n        case mChunk of\n          Nothing    -\u003e return ()          -- stream finished\n          Just chunk -\u003e do\n            BS.putStr chunk\n            loop\n  loop\n  resp.body.closeStream\n```\n\n### SSE (Server-Sent Events)\n\nUse `StreamBody SseEvent` to automatically parse an SSE stream. Each call to `readNext` returns the next complete event:\n\n```haskell\nimport Network.HTTP.Request\nimport qualified Data.Text.IO as T\n\ndata SseEvent = SseEvent\n  { sseData :: T.Text       -- content of the \"data:\" field\n  , sseType :: Maybe T.Text -- content of the \"event:\" field\n  , sseId   :: Maybe T.Text -- content of the \"id:\" field\n  }\n\nmain :: IO ()\nmain = do\n  let req = Request GET \"https://example.com/events\" [] (Nothing :: Maybe BS.ByteString)\n  resp \u003c- send req :: IO (Response (StreamBody SseEvent))\n  print resp.status  -- 200\n\n  let loop = do\n        mEvent \u003c- resp.body.readNext\n        case mEvent of\n          Nothing    -\u003e return ()          -- stream finished\n          Just event -\u003e do\n            T.putStrLn event.sseData\n            loop\n  loop\n  resp.body.closeStream\n```\n\n`StreamBody` has two fields:\n\n- `readNext :: IO (Maybe a)` — reads the next chunk or event; returns `Nothing` when the stream ends\n- `closeStream :: IO ()` — closes the underlying connection\n\n## API Documents\n\nSee the hackage page: http://hackage.haskell.org/package/request/docs/Network-HTTP-Request.html\n\n## About the Project\n\nRequest is \u0026copy; 2020-2026 by [AN Long](https://github.com/aisk).\n\n### License\n\nRequest is distributed by a [BSD license](https://github.com/aisk/request/tree/master/LICENSE).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faisk%2Fhaskell-request","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faisk%2Fhaskell-request","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faisk%2Fhaskell-request/lists"}