MCPcopy Index your code
hub / github.com/imroc/req

github.com/imroc/req @v3.59.0 sqlite

repository ↗ · DeepWiki ↗ · release v3.59.0 ↗
2,113 symbols 6,843 edges 123 files 1,099 documented · 52% 5 cross-repo links
README

req

Simple Go HTTP client with Black Magic

    <a href="https://github.com/imroc/req/actions/workflows/ci.yml?query=branch%3Amaster"><img src="https://github.com/imroc/req/actions/workflows/ci.yml/badge.svg" alt="Build Status"></a>
    <a href="https://goreportcard.com/report/github.com/imroc/req/v3"><img src="https://goreportcard.com/badge/github.com/imroc/req/v3" alt="Go Report Card"></a>
    <a href="https://pkg.go.dev/github.com/imroc/req/v3"><img src="https://pkg.go.dev/badge/github.com/imroc/req/v3.svg"></a>
    <a href="https://github.com/imroc/req/raw/v3.59.0/LICENSE"><img src="https://img.shields.io/github/license/imroc/req.svg" alt="License"></a>
    <a href="https://github.com/imroc/req/releases"><img src="https://img.shields.io/github/v/release/imroc/req?display_name=tag&sort=semver" alt="GitHub Releases"></a>
    <a href="https://github.com/avelino/awesome-go"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Go"></a>

Documentation

Full documentation is available on the official website: https://req.cool.

Features

  • Simple and Powerful: Simple and easy to use, providing rich client-level and request-level settings, all of which are intuitive and chainable methods.
  • Easy Debugging: Powerful and convenient debug utilities, including debug logs, performance traces, and even dump the complete request and response content (see Debugging).
  • Easy API Testing: API testing can be done with minimal code, no need to explicitly create any Request or Client, or even to handle errors (See Quick HTTP Test)
  • Smart by Default: Detect and decode to utf-8 automatically if possible to avoid garbled characters (See Auto Decode), marshal request body and unmarshal response body automatically according to the Content-Type.
  • Support Multiple HTTP Versions: Support HTTP/1.1, HTTP/2, and HTTP/3, and can automatically detect the server side and select the optimal HTTP version for requests, you can also force the protocol if you want (See Force HTTP version).
  • Support Retry: Support automatic request retry and is fully customizable (See Retry).
  • HTTP Fingerprinting: Support http fingerprint impersonation, so that we can access websites that prohibit crawler programs by identifying http fingerprints (See HTTP Fingerprint).
  • Multiple Authentication Methods: You can use HTTP Basic Auth, Bearer Auth Token and Digest Auth out of box (see Authentication).
  • Easy Download and Upload: You can download and upload files with simple request settings, and even set a callback to show real-time progress (See Download and Upload).
  • Exportable: req.Transport is exportable. Compared with http.Transport, it also supports HTTP3, dump content, middleware, etc. It can directly replace the Transport of http.Client in existing projects, and obtain more powerful functions with minimal code change.
  • Extensible: Support Middleware for Request, Response, Client and Transport (See Request and Response Middleware) and Client and Transport Middleware).

Get Started

Install

You first need Go installed (version 1.24+ is required), then you can use the below Go command to install req:

go get github.com/imroc/req/v3

Import

Import req to your code:

import "github.com/imroc/req/v3"

Basic Usage

# assume the following codes in main.go file
$ cat main.go
package main

import (
    "github.com/imroc/req/v3"
)

func main() {
    req.DevMode() // Treat the package name as a Client, enable development mode
    req.MustGet("https://httpbin.org/uuid") // Treat the package name as a Request, send GET request.

    req.EnableForceHTTP1() // Force using HTTP/1.1
    req.MustGet("https://httpbin.org/uuid")
}
$ go run main.go
2022/05/19 10:05:07.920113 DEBUG [req] HTTP/2 GET https://httpbin.org/uuid
:authority: httpbin.org
:method: GET
:path: /uuid
:scheme: https
user-agent: req/v3 (https://github.com/imroc/req/v3)
accept-encoding: gzip

:status: 200
date: Thu, 19 May 2022 02:05:08 GMT
content-type: application/json
content-length: 53
server: gunicorn/19.9.0
access-control-allow-origin: *
access-control-allow-credentials: true

{
  "uuid": "bd519208-35d1-4483-ad9f-e1555ae108ba"
}

2022/05/19 10:05:09.340974 DEBUG [req] HTTP/1.1 GET https://httpbin.org/uuid
GET /uuid HTTP/1.1
Host: httpbin.org
User-Agent: req/v3 (https://github.com/imroc/req/v3)
Accept-Encoding: gzip

HTTP/1.1 200 OK
Date: Thu, 19 May 2022 02:05:09 GMT
Content-Type: application/json
Content-Length: 53
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "uuid": "49b7f916-c6f3-49d4-a6d4-22ae93b71969"
}

The sample code above is good for quick testing purposes, which use DevMode() to see request details, and send requests using global wrapper methods that use the default client behind the scenes to initiate the request.

In production, it is recommended to explicitly create a client, and then use the same client to send all requests, please see other examples below.

Videos

The following is a series of video tutorials for req:

More

Check more introduction, tutorials, examples, best practices and API references on the official website.

Simple GET

package main

import (
    "fmt"
    "github.com/imroc/req/v3"
    "log"
)

func main() {
    client := req.C() // Use C() to create a client.
    resp, err := client.R(). // Use R() to create a request.
        Get("https://httpbin.org/uuid")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(resp)
}
{
  "uuid": "a4d4430d-0e5f-412f-88f5-722d84bc2a62"
}

Advanced GET

package main

import (
  "fmt"
  "github.com/imroc/req/v3"
  "log"
  "time"
)

type ErrorMessage struct {
  Message string `json:"message"`
}

type UserInfo struct {
  Name string `json:"name"`
  Blog string `json:"blog"`
}

func main() {
  client := req.C().
    SetUserAgent("my-custom-client"). // Chainable client settings.
    SetTimeout(5 * time.Second)

  var userInfo UserInfo
  var errMsg ErrorMessage
  resp, err := client.R().
    SetHeader("Accept", "application/vnd.github.v3+json"). // Chainable request settings.
    SetPathParam("username", "imroc"). // Replace path variable in url.
    SetSuccessResult(&userInfo). // Unmarshal response body into userInfo automatically if status code is between 200 and 299.
    SetErrorResult(&errMsg). // Unmarshal response body into errMsg automatically if status code >= 400.
    EnableDump(). // Enable dump at request level, only print dump content if there is an error or some unknown situation occurs to help troubleshoot.
    Get("https://api.github.com/users/{username}")

  if err != nil { // Error handling.
    log.Println("error:", err)
    log.Println("raw content:")
    log.Println(resp.Dump()) // Record raw content when error occurs.
    return
  }

  if resp.IsErrorState() { // Status code >= 400.
    fmt.Println(errMsg.Message) // Record error message returned.
    return
  }

  if resp.IsSuccessState() { // Status code is between 200 and 299.
    fmt.Printf("%s (%s)\n", userInfo.Name, userInfo.Blog)
    return
  }

  // Unknown status code.
  log.Println("unknown status", resp.Status)
  log.Println("raw content:")
  log.Println(resp.Dump()) // Record raw content when server returned unknown status code.
}

Normally it will output (SuccessState):

roc (https://imroc.cc)

More Advanced GET

You can set up a unified logic for error handling on the client, so that each time you send a request you only need to focus on the success situation, reducing duplicate code.

package main

import (
    "fmt"
    "github.com/imroc/req/v3"
    "log"
    "time"
)

type ErrorMessage struct {
    Message string `json:"message"`
}

func (msg *ErrorMessage) Error() string {
    return fmt.Sprintf("API Error: %s", msg.Message)
}

type UserInfo struct {
    Name string `json:"name"`
    Blog string `json:"blog"`
}

var client = req.C().
    SetUserAgent("my-custom-client"). // Chainable client settings.
    SetTimeout(5 * time.Second).
    EnableDumpEachRequest().
    SetCommonErrorResult(&ErrorMessage{}).
    OnAfterResponse(func(client *req.Client, resp *req.Response) error {
        if resp.Err != nil { // There is an underlying error, e.g. network error or unmarshal error.
            return nil
        }
        if errMsg, ok := resp.ErrorResult().(*ErrorMessage); ok {
            resp.Err = errMsg // Convert api error into go error
            return nil
        }
        if !resp.IsSuccessState() {
            // Neither a success response nor a error response, record details to help troubleshooting
            resp.Err = fmt.Errorf("bad status: %s\nraw content:\n%s", resp.Status, resp.Dump())
        }
        return nil
    })

func main() {
    var userInfo UserInfo
    resp, err := client.R().
        SetHeader("Accept", "application/vnd.github.v3+json"). // Chainable request settings
        SetPathParam("username", "imroc").
        SetSuccessResult(&userInfo). // Unmarshal response body into userInfo automatically if status code is between 200 and 299.
        Get("https://api.github.com/users/{username}")

    if err != nil { // Error handling.
        log.Println("error:", err)
        return
    }

    if resp.IsSuccessState() { // Status code is between 200 and 299.
        fmt.Printf("%s (%s)\n", userInfo.Name, userInfo.Blog)
    }
}

Simple POST

package main

import (
  "fmt"
  "github.com/imroc/req/v3"
  "log"
)

type Repo struct {
  Name string `json:"name"`
  Url  string `json:"url"`
}

type Result struct {
  Data string `json:"data"`
}

func main() {
  client := req.C().DevMode()
  var result Result

  resp, err := client.R().
    SetBody(&Repo{Name: "req", Url: "https://github.com/imroc/req"}).
    SetSuccessResult(&result).
    Post("https://httpbin.org/post")
  if err != nil {
    log.Fatal(err)
  }

  if !resp.IsSuccessState() {
    fmt.Println("bad response status:", resp.Status)
    return
  }
  fmt.Println("++++++++++++++++++++++++++++++++++++++++++++++++")
  fmt.Println("data:", result.Data)
  fmt.Println("++++++++++++++++++++++++++++++++++++++++++++++++")
}
2022/05/19 20:11:00.151171 DEBUG [req] HTTP/2 POST https://httpbin.org/post
:authority: httpbin.org
:method: POST
:path: /post
:scheme: https
user-agent: req/v3 (https://github.com/imroc/req/v3)
content-type: application/json; charset=utf-8
content-length: 55
accept-encoding: gzip

{"name":"req","website":"https://github.com/imroc/req"}

:status: 200
date: Thu, 19 May 2022 12:11:00 GMT
content-type: application/json
content-length: 651
server: gunicorn/19.9.0
access-control-allow-origin: *
access-control-allow-credentials: true

{
  "args": {},
  "data": "{\"name\":\"req\",\"website\":\"https://github.com/imroc/req\"}",
  "files": {},
  "form": {},
  "headers": {
    "Accept-Encoding": "gzip",
    "Content-Length": "55",
    "Content-Type": "application/json; charset=utf-8",
    "Host": "httpbin.org",
    "User-Agent": "req/v3 (https://github.com/imroc/req/v3)",
    "X-Amzn-Trace-Id": "Root=1-628633d4-7559d633152b4307288ead2e"
  },
  "json": {
    "name": "req",
    "website": "https://github.com/imroc/req"
  },
  "origin": "103.7.29.30",
  "url": "https://httpbin.org/post"
}

++++++++++++++++++++++++++++++++++++++++++++++++
data: {"name":"req","url":"https://github.com/imroc/req"}
++++++++++++++++++++++++++++++++++++++++++++++++

Do API Style

If you like, you can also use a Do API style like the following to make requests:

package main

import (
    "fmt"
    "github.com/imroc/req/v3"
)

type APIResponse struct {
    Origin string `json:"origin"`
    Url    string `json:"url"`
}

func main() {
    var resp APIResponse
    c := req.C().SetBaseURL("https://httpbin.org/post")
    err := c.Post().
        SetBody("hello").
        Do().
        Into(&resp)
    if err != nil {
        panic(err)
    }
    fmt.Println("My IP is", resp.Origin)
}
My IP is 182.138.155.113
  • The order of chain calls is more intuitive: first call Client to create a request with a specified Method, then use chain calls to set the request, then use Do() to fire the request, return Response, and finally call Response.Into to unmarshal response body into specified object.
  • Response.Into will return an error if an error occurs during sending the request or during unmarshalling.
  • The url of some APIs is fixed, and different types of requests are implemented by passing different bodies. In this scenario, Client.SetBaseURL can be used to set a unified url, and there is no need to set the url for each request when initiating a request. Of course, you can also call Request.SetURL to set it if you need it.

Build SDK With Req

Here is an example of building GitHub's SDK with req, using two styles (GetUserProfile_Style1, GetUserProfile_Style2).

```go import ( "context" "fmt" "github.com/imroc/req/v3" )

type ErrorMessage struct { Message string json:"message" }

Extension points exported contracts — how you extend this code

RoundTripper (Interface)
RoundTripper is the interface of req's Client. [8 implementers]
client.go
Writer (Interface)
Writer is the same interface as io.Writer. It is duplicated here to avoid importing io. [15 implementers]
internal/bisect/bisect.go
Logger (Interface)
Logger is the abstract logging interface, gives control to the Req users, choice of the logger. [2 implementers]
logger.go
Options (Interface)
Options controls the dump behavior. [2 implementers]
internal/dump/dump.go
CompressReader (Interface)
(no doc) [4 implementers]
internal/compress/reader.go
Settingser (Interface)
Settingser allows waiting for and retrieving the peer's HTTP/3 settings. [2 implementers]
internal/http3/body.go
Jar (Interface)
Jar is a container of AltSvc. [1 implementers]
pkg/altsvc/jar.go
ClientConnPool (Interface)
ClientConnPool manages a pool of HTTP/2 client connections. [1 implementers]
internal/http2/client_conn_pool.go

Core symbols most depended-on inside this repo

R
called by 209
client.go
AssertEqual
called by 191
internal/tests/assert.go
Get
called by 183
client.go
Unlock
called by 139
transport.go
Errorf
called by 133
logger.go
Lock
called by 127
transport.go
String
called by 86
trace.go
Write
called by 81
internal/bisect/bisect.go

Shape

Method 1,043
Function 805
Struct 198
TypeAlias 30
Interface 19
FuncType 18

Languages

Go100%

Modules by API surface

transport.go190 symbols
client.go180 symbols
client_wrapper.go134 symbols
internal/http2/transport.go133 symbols
internal/http2/frame.go113 symbols
request.go111 symbols
request_wrapper.go88 symbols
client_test.go64 symbols
request_test.go56 symbols
internal/dump/dump.go44 symbols
transfer.go42 symbols
internal/http3/stream.go38 symbols

Dependencies from manifests, versioned

github.com/andybalholm/brotliv1.2.0 · 1×
github.com/cloudflare/circlv1.6.1 · 1×
github.com/gin-contrib/ssev0.1.0 · 1×
github.com/go-logr/logrv1.2.3 · 1×
github.com/go-logr/stdrv1.2.2 · 1×
github.com/go-playground/localesv0.14.0 · 1×
github.com/go-playground/universal-translatorv0.18.0 · 1×
github.com/google/go-querystringv1.1.0 · 1×

For agents

$ claude mcp add req \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact