{"id":13543107,"url":"https://github.com/lucperkins/rek","last_synced_at":"2025-05-14T21:03:00.916Z","repository":{"id":75187310,"uuid":"261092900","full_name":"lucperkins/rek","owner":"lucperkins","description":"An easy HTTP client for Go. Inspired by the immortal Requests.","archived":false,"fork":false,"pushed_at":"2024-10-21T09:33:26.000Z","size":77,"stargazers_count":384,"open_issues_count":0,"forks_count":19,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-10-21T13:38:59.400Z","etag":null,"topics":["go","golang","http-client","python-requests","requests"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/lucperkins/rek?tab=doc","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lucperkins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["lucperkins"]}},"created_at":"2020-05-04T06:00:49.000Z","updated_at":"2024-10-21T09:33:30.000Z","dependencies_parsed_at":"2024-10-22T10:53:57.709Z","dependency_job_id":null,"html_url":"https://github.com/lucperkins/rek","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucperkins%2Frek","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucperkins%2Frek/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucperkins%2Frek/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucperkins%2Frek/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lucperkins","download_url":"https://codeload.github.com/lucperkins/rek/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247485279,"owners_count":20946398,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["go","golang","http-client","python-requests","requests"],"created_at":"2024-08-01T11:00:23.166Z","updated_at":"2025-04-06T13:06:14.753Z","avatar_url":"https://github.com/lucperkins.png","language":"Go","readme":"# rek\n\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=gostyle=flat)](https://pkg.go.dev/mod/github.com/lucperkins/rek)\n\nAn easy HTTP client for [Go](https://golang.org) inspired by [Requests](https://requests.readthedocs.io/en/master/), plus all the Go-specific goodies you'd hope for in a client. Here's an example:\n\n```go\n// GET request\nres, _ := rek.Get(\"https://httpbin.org/get\")\n\nfmt.Println(res.StatusCode())\n\nbody, _ := rek.BodyAsString(res.Body())\n\nfmt.Println(body)\n\n// POST request\ntype Comment struct {\n    Username string `json:\"username\"`\n    Body     string `json:\"body\"`\n}\n\nres, _ := rek.Post(\"https://httpbin.org/post\",\n    rek.Json(Comment{Username: \"genesiskel\", Body: \"This movie sucked. Two thumbs down.\"}),\n    rek.Headers(map[string]string{\"My-Custom-Header\": \"foo,bar,baz\"}),\n    rek.BasicAuth(\"user\", \"pass\"),\n    rek.Timeout(5 * time.Second),\n)\n\nfmt.Println(res.StatusCode())\n\nbody, _ := rek.BodyAsString(res.Body())\nfmt.Println(body)\n```\n\n## Responses\n\nThe `Response` struct has the following methods:\n\n\nMethod | Description | Return type\n:------|:------------|:-----------\n`StatusCode()` | HTTP status code, e.g. 200, 400 | `int`\n`Body()` | The HTTP response body as a reader | [`io.ReadCloser`](https://pkg.go.dev/io?tab=doc#ReadCloser)\n`Headers()` | The response headers | `map[string]string`\n`Encoding()` | The content encoding of the response body, e.g. `gzip` | `[]string`\n`ContentType()` | The value of the `Content-Type` header | `string`\n`Raw()` | The unmodified [`*http.Response`](https://pkg.go.dev/net/http?tab=doc#Response) | [`*http.Response`](https://pkg.go.dev/net/http?tab=doc#Response)\n`Cookies()` | The cookies attached to the response | `[]*http.Cookie`\n`ContentLength()` | The length of the response | `int64`\n`Status()` | The status of the response, e.g. `200 OK` | `string`\n\n### HTTP response body\n\nKeep in mind that the HTTP response body **can only be read once**. This is one area in which rek does *not* directly correspond to the [Requests](https://requests.readthedocs.io/en/master/) API. And so this, for example, won't work the way you might want:\n\n```go\nres, _ := rek.Get(\"https://httpbin.org/get\")\n\nbs1, _ := ioutil.ReadAll(res.Body()) // Non-empty byte slice\n\nbs2, _ := ioutil.ReadAll(res.Body()) // Empty byte slice\n```\n\nIf you'd like to use the response body more than once, store it in a variable rather than re-accessing the body.\n\n### Helper methods\n\nThere are two simple helper methods for working with the response body:\n\nFunction | Return types\n:--------|:------------\n`BodyAsString(io.ReadCloser)` | `(string, error)`\n`BodyAsBytes(io.ReadCloser)` | `([]byte, error)`\n\nBear in mind the caveat mentioned above, that the request body can only be read once, still holds. Here are some examples:\n\n```go\nres, _ := rek.Get(\"https://httpbin.org/get\")\n\ns1, _ := rek.BodyAsString(res.Body()) // body is read here\n\ns2, _ := rek.BodyAsString(res.Body()) // s2 is an empty string\n```\n\n### Non-standard methods\n\nrek natively supports the methods `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, and `HEAD`. If you'd like to use a method that's not directly supported, you can use the [`Do`](https://pkg.go.dev/github.com/lucperkins/rek?tab=doc#Do/) function:\n\n```go\nres, err := rek.Do(\"SOMEOTHERMETHOD\", rek.Timeout(10 * time.Second))\n```\n\n## Options\n\n### Headers\n\n```go\nheaders := map[string]string{\n    \"My-Custom-Header\": \"foo,bar,baz\",\n}\n\nres, err := rek.Post(\"https://httpbin.org/post\", rek.Headers(headers))\n```\n\n### JSON\n\nPass in any struct:\n\n```go\ntype Comment struct {\n    ID        int64     `json:\"id\"`\n    Body      string    `json:\"body\"`\n    Timestamp time.Time `json:\"timestamp\"`\n}\n\ncomment := Comment{ID:47, Body:\"Cool movie!\", Timestamp: time.Now()}\n\nres, err := rek.Post(\"https://httpbin.org/post\", rek.Json(comment))\n```\n\n\u003e Request headers are automatically updated to include `Content-Type` as `application/json;charset=utf-8`.\n\n### Request timeout\n\n```go\nres, err := rek.Get(\"https://httpbin.org/get\", rek.Timeout(5 * time.Second))\n```\n\n### Form data\n\n```go\nform := map[string]string{\n    \"foo\": \"bar\",\n    \"baq\": \"baz\",\n}\n\nres, err := rek.Put(\"https://httpbin.org/put\", rek.FormData(form))\n```\n\n\u003e Request headers are automatically updated to include `Content-Type` as `application/x-www-form-urlencoded`.\n\n### File upload\n\n```go\nfieldName := \"file\"\nfilepath := \"docs/README.md\"\nparams := nil\n\nres, err := rek.Post(\"https:/httpbin.org/post\", rek.File(fieldName, filepath, params))\n```\n\n\u003e Request headers are automatically updated to include `Content-Type` as `multipart/form-data; boundary=...`.\n\n### Basic auth\n\n```go\nusername, password := \"user\", \"pass\"\n\nres, _ := rek.Get(fmt.Sprintf(\"https://httpbin.org/basic-auth/%s/%s\", username, password),\n    rek.BasicAuth(username, password))\n\nfmt.Println(res.StatusCode()) // 200\n\nres, _ := rek.Get(fmt.Sprintf(\"https://httpbin.org/basic-auth/%s/other\", username, password),\n    rek.BasicAuth(username, password))\n\nfmt.Println(res.StatusCode()) // 401\n```\n\n### Data\n\nTakes any input and serializes it to a `[]byte`:\n\n```go\ndata := map[string]interface{\n    \"age\": 38,\n    \"name\": \"Luc\",\n}\n\nres, err := rek.Post(\"https://httpbin.org/post\", rek.Data(data))\n```\n\n\u003e Request headers are automatically updated to include `Content-Type` as `application/octet-stream`.\n\n### User agent\n\n```go\nres, err := rek.Post(\"https://httpbin.org/post\", rek.UserAgent(\"ThisGuy\"))\n```\n\n### Bearer (useful for JSON Web Tokens)\n\n```go\ntoken := \"... token ...\"\n\nres, err := rek.Post(\"https://httpbin.org/post\", rek.Bearer(token))\n```\n\n### Request modifier\n\nSupply a function that modifies the [`*http.Request`](https://pkg.go.dev/net/http?tab=doc#Request) (after all other supplied options have been applied to the request):\n\n```go\nmodifier := func(r *http.Request) {\n   // Do whatever you want with the request\n}\n\nres, err := rek.Get(\"https://httpbin.org/get\", rek.RequestModifier(modifier))\n```\n\n### Accept\n\nApply an `Accept` header to the request:\n\n```go\nres, err := rek.Get(\"https://httpbin.org/get\", rek.Accept(\"application/tar+gzip\"))\n```\n\n### API key\n\nAdd an API key to the request as an `Authorization` header (where the value is `Basic ${KEY}`):\n\n```go\nres, err := rek.Get(\"https://some-secure-api.io\", rek.ApiKey(\"a1b2c3...\"))\n```\n\n### Context\n\nSupply a [`Context`](https://pkg.go.dev/context?tab=doc#Context) to the request:\n\n```go\nctx, cancel := context.WithCancel(context.Background())\n\nres, err := rek.Get(\"https://long-winded-api.io\", rek.Context(ctx))\n\n// Ugh, I don't want this request to happen anymore\n\ncancel()\n```\n\n### OAuth2\n\nYou can add an OAuth2 configuration and token to your request:\n\n```go\nconf := \u0026oauth2.Config{\n\tClientID:     \"YOUR_CLIENT_ID\",\n\tClientSecret: \"YOUR_CLIENT_SECRET\",\n\tScopes:       []string{\"SCOPE1\", \"SCOPE2\"},\n\tEndpoint: oauth2.Endpoint{\n\t\tAuthURL:  \"https://provider.com/o/oauth2/auth\",\n\t\tTokenURL: \"https://provider.com/o/oauth2/token\",\n\t},\n}\n\ntok, err := conf.Exchange(ctx, code)\n// handle error\n\nres, err := rek.Get(\"https://oauth2-protected-site.com\", rek.OAuth2(conf, tok))\n```\n\n## Custom client\n\nYou can pass in your own `*http.Client` as the basis for the request:\n\n```go\nclient := \u0026http.Client{\n    // Custom attributes\n}\n\nres, err := rek.Get(\"https://httpbin.org/get\", rek.Client(client))\n```\n\n\n## Validation\n\nIt's important to bear in mind that rek provides *no validation* for the options that you provide on a specific request and doesn't provide any constraints on which options can be used with which request method. Some options may not make sense for some methods, e.g. request JSON on a `HEAD` request, but I leave it up to the end user to supply their own constraints. One exception is that the request body can only be set once. If you attempt to set it more than once you'll get a `ErrRequestBodySetMultipleTimes` error. This, for example, will throw that error:\n\n```go\ncomment := Comment{Body: \"This movie sucked\"}\n\n_, err := rek.Post(\"https://httpbin.org/post\",\n    rek.Json(comment),\n    rek.FormData(map[string]string{\"foo\": \"bar\"}))\n\nfmt.Println(errors.Is(err, rek.ErrRequestBodySetMultipleTimes)) // true\n```\n","funding_links":["https://github.com/sponsors/lucperkins"],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucperkins%2Frek","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucperkins%2Frek","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucperkins%2Frek/lists"}