diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -16,9 +16,40 @@
 import Network.HTTP.Request
 
 resp <- get "https://api.leancloud.cn/1.1/date"
-print $ requestStatus resp
+print $ responseStatus resp
 ```
 
+## Record Dot Syntax Support
+
+This library supports modern Haskell record dot syntax. To use it, enable these language extensions:
+
+```haskell
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+```
+
+### Creating Records with Dot Syntax
+
+```haskell
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+
+import Network.HTTP.Request
+import qualified Data.ByteString as BS
+
+-- Create request using record dot syntax
+let req = Request { method = GET, url = "https://api.leancloud.cn/1.1/date", headers = [], body = Nothing }
+
+-- 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.
@@ -28,11 +59,11 @@
 `Request` is all about the information you will send to the target URL.
 
 ```haskell
-data Request = Request
-  { requestMethod  :: Method
-  , requestUrl     :: String
-  , requestHeaders :: Headers
-  , requestBody    :: Maybe Data.ByteString.ByteString
+data Request a = Request
+  { method  :: Method
+  , url     :: String
+  , headers :: Headers
+  , body    :: Maybe a
   } deriving (Show)
 ```
 
@@ -41,7 +72,7 @@
 Once you have constructed your own `Request` record, you can call the `send` function to send it to the server. The `send` function's type is:
 
 ```haskell
-send :: Request -> IO Response
+send :: (IsString a) => Request a -> IO (Response a)
 ```
 
 ### Response
@@ -49,17 +80,26 @@
 `Response` is what you got from the server URL.
 
 ```haskell
-data Response = Response
-  { responseStatus  :: Int
-  , responseHeaders :: Headers
-  , responseBody    :: Data.ByteString.ByteString
+data Response a = Response
+  { status  :: Int
+  , headers :: Headers
+  , body    :: a
   } deriving (Show)
 ```
 
+The response body type `a` can be any type that implements the `IsString` constraint, allowing flexible handling of response data.
+
+### Backward Compatibility
+
+For users who prefer not to use the language extensions, you can still:
+
+- Create requests using positional arguments: `Request GET "url" [] Nothing`
+- Use prefixed accessor functions: `requestStatus response`, `requestHeaders response`, etc.
+
 ### Example
 
 ```haskell
-:set -XOverloadedStrings
+{-# LANGUAGE OverloadedStrings #-}
 
 import Network.HTTP.Request
 
@@ -67,8 +107,8 @@
 let req = Request GET "https://api.leancloud.cn/1.1/date" [] Nothing
 -- Send it.
 res <- send req
--- access the fields on Response.
-print $ requestStatus resp
+-- Access the fields on Response.
+print $ responseStatus res
 ```
 
 ## Shortcuts
@@ -76,21 +116,21 @@
 As you expected, there are some shortcuts for the most used scenarios.
 
 ```haskell
-get :: String -> IO Response
+get :: String -> IO (Response BS.ByteString)
 get url =
-  send $ Request GET url [] Nothing
+  send $ Request { method = GET, url = url, headers = [], body = Nothing }
 
-delete :: String -> IO Response
+delete :: String -> IO (Response BS.ByteString)
 delete url =
-  send $ Request DELETE url [] Nothing
+  send $ Request { method = DELETE, url = url, headers = [], body = Nothing }
 
-post :: (String, Maybe Data.ByteString.ByteString) -> IO Response
+post :: (String, Maybe BS.ByteString) -> IO (Response BS.ByteString)
 post (url, body) =
-  send $ Request POST url [] body
+  send $ Request { method = POST, url = url, headers = [], body = body }
 
-put :: (String, Maybe Data.ByteString.ByteString) -> IO Response
+put :: (String, Maybe BS.ByteString) -> IO (Response BS.ByteString)
 put (url, body) =
-  send $ Request PUT url [] body
+  send $ Request { method = PUT, url = url, headers = [], body = body }
 ```
 
 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.
diff --git a/request.cabal b/request.cabal
--- a/request.cabal
+++ b/request.cabal
@@ -1,5 +1,5 @@
 name:                request
-version:             0.2.3.0
+version:             0.3.0.0
 -- synopsis:
 description:         "HTTP client for haskell, inpired by requests and http-dispatch."
 homepage:            https://github.com/aisk/request#readme
@@ -35,4 +35,5 @@
   build-depends:       base
                      , request
                      , hspec
+                     , bytestring         >= 0.10.12 && < 0.13
   default-language:    Haskell2010
diff --git a/src/Network/HTTP/Request.hs b/src/Network/HTTP/Request.hs
--- a/src/Network/HTTP/Request.hs
+++ b/src/Network/HTTP/Request.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Network.HTTP.Request
@@ -11,6 +13,13 @@
     post,
     put,
     send,
+    requestMethod,
+    requestUrl,
+    requestHeaders,
+    requestBody,
+    responseStatus,
+    responseHeaders,
+    responseBody,
   )
 where
 
@@ -51,13 +60,26 @@
   show (Method method) = method
 
 data Request a = Request
-  { requestMethod :: Method,
-    requestUrl :: String,
-    requestHeaders :: Headers,
-    requestBody :: Maybe a
+  { method :: Method,
+    url :: String,
+    headers :: Headers,
+    body :: Maybe a
   }
   deriving (Show)
 
+-- Compatibility accessor functions
+requestMethod :: Request a -> Method
+requestMethod req = req.method
+
+requestUrl :: Request a -> String
+requestUrl req = req.url
+
+requestHeaders :: Request a -> Headers
+requestHeaders req = req.headers
+
+requestBody :: Request a -> Maybe a
+requestBody req = req.body
+
 toLowlevelRequest :: (S.IsString a) => Request a -> IO LowLevelClient.Request
 toLowlevelRequest req = do
   initReq <- LowLevelClient.parseRequest $ requestUrl req
@@ -67,17 +89,27 @@
         LowLevelClient.requestHeaders = map (\(k, v) -> (CI.mk k, v)) $ requestHeaders req
       }
 
-data Response = Response
-  { responseStatus :: Int,
-    responseHeaders :: Headers,
-    responseBody :: BS.ByteString
+data Response a = Response
+  { status :: Int,
+    headers :: Headers,
+    body :: a
   }
   deriving (Show)
 
-fromLowLevelRequest :: LowLevelClient.Response LBS.ByteString -> Response
+-- Compatibility accessor functions for Response
+responseStatus :: Response a -> Int
+responseStatus res = res.status
+
+responseHeaders :: Response a -> Headers
+responseHeaders res = res.headers
+
+responseBody :: Response a -> a
+responseBody res = res.body
+
+fromLowLevelRequest :: (S.IsString a) => LowLevelClient.Response LBS.ByteString -> Response a
 fromLowLevelRequest res =
   let status = LowLevelStatus.statusCode . LowLevelClient.responseStatus $ res
-      body = LBS.toStrict $ LowLevelClient.responseBody res
+      body = S.fromString . C.unpack . LBS.toStrict $ LowLevelClient.responseBody res
       headers = LowLevelClient.responseHeaders res
    in Response
         status
@@ -90,29 +122,29 @@
         )
         body
 
-send :: (S.IsString a) => Request a -> IO Response
+send :: (S.IsString a) => Request a -> IO (Response a)
 send req = do
   manager <- LowLevelTLSClient.getGlobalManager
   llreq <- toLowlevelRequest req
   llres <- LowLevelClient.httpLbs llreq manager
   return $ fromLowLevelRequest llres
 
-get :: String -> IO Response
+get :: String -> IO (Response BS.ByteString)
 get url =
   send $ Request GET url [] Nothing
 
-delete :: String -> IO Response
+delete :: String -> IO (Response BS.ByteString)
 delete url =
   send $ Request DELETE url [] Nothing
 
-post :: (String, Maybe BS.ByteString) -> IO Response
+post :: (String, Maybe BS.ByteString) -> IO (Response BS.ByteString)
 post (url, body) =
   send $ Request POST url [] body
 
-put :: (String, Maybe BS.ByteString) -> IO Response
+put :: (String, Maybe BS.ByteString) -> IO (Response BS.ByteString)
 put (url, body) =
   send $ Request PUT url [] body
 
-patch :: (String, Maybe BS.ByteString) -> IO Response
+patch :: (String, Maybe BS.ByteString) -> IO (Response BS.ByteString)
 patch (url, body) =
   send $ Request PATCH url [] body
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,9 +1,12 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Main where
 
 import Network.HTTP.Request
 import Test.Hspec
+import qualified Data.ByteString as BS
 
 main :: IO ()
 main = hspec $ do
@@ -27,3 +30,20 @@
     it "should patch to httpbin.org/patch and return 200 OK" $ do
       response <- patch ("https://postman-echo.com/patch", Just "Hello!")
       responseStatus response `shouldBe` 200
+
+    it "should use dot record syntax to create and access request/response" $ do
+      let req = Request { method = GET, url = "http://example.com", headers = [("User-Agent", "Haskell-Request")], body = Nothing }
+      response <- send req
+      response.status `shouldBe` 200
+      response.headers `shouldSatisfy` (not . null)
+
+    it "should access response body with different IsString types" $ do
+      -- Test with ByteString body
+      let req1 = Request { method = GET, url = "http://example.com", headers = [], body = Nothing }
+      response1 <- send req1 :: IO (Response BS.ByteString)
+      BS.length response1.body `shouldSatisfy` (> 0)
+
+      -- Test with String body
+      let req2 = Request { method = GET, url = "http://example.com", headers = [], body = Nothing }
+      response2 <- send req2 :: IO (Response String)
+      not (null response2.body) `shouldBe` True
