{"id":13413652,"url":"https://github.com/gojek/heimdall","last_synced_at":"2026-04-09T07:27:31.913Z","repository":{"id":37366321,"uuid":"118105591","full_name":"gojek/heimdall","owner":"gojek","description":"An enhanced HTTP client for Go","archived":false,"fork":false,"pushed_at":"2024-05-29T23:40:33.000Z","size":246,"stargazers_count":2670,"open_issues_count":50,"forks_count":213,"subscribers_count":55,"default_branch":"master","last_synced_at":"2025-05-07T00:06:04.106Z","etag":null,"topics":["backoff","circuit-breaker","distributed-systems","golang","httpclient","hystrix","retries","scale"],"latest_commit_sha":null,"homepage":"http://gojek.tech","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gojek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-19T09:32:26.000Z","updated_at":"2025-05-02T19:32:37.000Z","dependencies_parsed_at":"2024-04-05T10:24:19.870Z","dependency_job_id":"512fde85-3ee2-4634-a8e0-4a96041f96bf","html_url":"https://github.com/gojek/heimdall","commit_stats":{"total_commits":155,"total_committers":32,"mean_commits":4.84375,"dds":0.6580645161290323,"last_synced_commit":"bce641e4e4b1d014c7c864bfae9f3019f47de1f1"},"previous_names":["gojektech/heimdall"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gojek%2Fheimdall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gojek%2Fheimdall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gojek%2Fheimdall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gojek%2Fheimdall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gojek","download_url":"https://codeload.github.com/gojek/heimdall/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253384827,"owners_count":21899932,"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":["backoff","circuit-breaker","distributed-systems","golang","httpclient","hystrix","retries","scale"],"created_at":"2024-07-30T20:01:45.470Z","updated_at":"2026-04-09T07:27:31.905Z","avatar_url":"https://github.com/gojek.png","language":"Go","funding_links":[],"categories":["Go","Networking","HTTP Clients"],"sub_categories":["HTTP Clients"],"readme":"# Heimdall\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"doc/heimdall-logo.png\" width=\"360\"\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/gojek/heimdall/actions\"\u003e\u003cimg src=\"https://github.com/gojek/heimdall/actions/workflows/ci.yml/badge.svg\" alt=\"Build Status\"\u003e\u003c/img\u003e\u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/gojek/heimdall\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/gojek/heimdall\"\u003e\u003c/img\u003e\u003c/a\u003e\n  \u003ca href=\"https://golangci.com\"\u003e\u003cimg src=\"https://golangci.com/badges/github.com/gojek/heimdall.svg\"\u003e\u003c/img\u003e\u003c/a\u003e\n  \u003ca href=\"https://coveralls.io/github/gojek/heimdall?branch=master\"\u003e\u003cimg src=\"https://coveralls.io/repos/github/gojek/heimdall/badge.svg?branch=master\"\u003e\u003c/img\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n* [Description](#description)\n* [Installation](#installation)\n* [Usage](#usage)\n  + [Making a simple `GET` request](#making-a-simple-get-request)\n  + [Creating a hystrix-like circuit breaker](#creating-a-hystrix-like-circuit-breaker)\n  + [Creating a hystrix-like circuit breaker with fallbacks](#creating-a-hystrix-like-circuit-breaker-with-fallbacks)\n  + [Creating an HTTP client with a retry mechanism](#creating-an-http-client-with-a-retry-mechanism)\n  + [Custom retry mechanisms](#custom-retry-mechanisms)\n  + [Custom HTTP clients](#custom-http-clients)\n* [Plugins](#plugins)\n* [Documentation](#documentation)\n* [FAQ](#faq)\n* [License](#license)\n\n## Description\n\nHeimdall is an HTTP client that helps your application make a large number of requests, at scale. With Heimdall, you can:\n- Use a [hystrix-like](https://github.com/afex/hystrix-go) circuit breaker to control failing requests\n- Add synchronous in-memory retries to each request, with the option of setting your own retrier strategy\n- Create clients with different timeouts for every request\n\nAll HTTP methods are exposed as a fluent interface.\n\n## Installation\n```\ngo get -u github.com/gojek/heimdall/v7\n```\n\n## Usage\n\n### Importing the package\n\nThis package can be used by adding the following import statement to your `.go` files.\n\n```go\nimport \"github.com/gojek/heimdall/v7/httpclient\" \n```\n\n### Making a simple `GET` request\nThe below example will print the contents of the google home page:\n\n```go\n// Create a new HTTP client with a default timeout\ntimeout := 1000 * time.Millisecond\nclient := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout))\n\n// Use the clients GET method to create and execute the request\nres, err := client.Get(\"http://google.com\", nil)\nif err != nil{\n\tpanic(err)\n}\n\n// Heimdall returns the standard *http.Response object\nbody, err := ioutil.ReadAll(res.Body)\nfmt.Println(string(body))\n```\n\nYou can also use the `*http.Request` object with the `http.Do` interface :\n\n```go\ntimeout := 1000 * time.Millisecond\nclient := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout))\n\n// Create an http.Request instance\nreq, _ := http.NewRequest(http.MethodGet, \"http://google.com\", nil)\n// Call the `Do` method, which has a similar interface to the `http.Do` method\nres, err := client.Do(req)\nif err != nil {\n\tpanic(err)\n}\n\nbody, err := ioutil.ReadAll(res.Body)\nfmt.Println(string(body))\n```\n\n### Creating a hystrix-like circuit breaker\n\nTo import hystrix package of heimdall.\n\n```go\nimport \"github.com/gojek/heimdall/v7/hystrix\"\n```\n\nYou can use the `hystrix.NewClient` function to create a client wrapped in a hystrix-like circuit breaker:\n\n```go\n// Create a new hystrix-wrapped HTTP client with the command name, along with other required options\nclient := hystrix.NewClient(\n\thystrix.WithHTTPTimeout(10 * time.Millisecond),\n\thystrix.WithCommandName(\"google_get_request\"),\n\thystrix.WithHystrixTimeout(1000 * time.Millisecond),\n\thystrix.WithMaxConcurrentRequests(30),\n\thystrix.WithErrorPercentThreshold(20),\n\thystrix.WithStatsDCollector(\"localhost:8125\", \"myapp.hystrix\"),\n)\n\n// The rest is the same as the previous example\n```\n\nIn the above example, there are two timeout values used: one for the hystrix configuration, and one for the HTTP client configuration. The former determines the time at which hystrix should register an error, while the latter determines when the client itself should return a timeout error. Unless you have any special requirements, both of these would have the same values.\n\nYou can choose to export hystrix metrics to a statsD collector with the `hystrix.WithStatsDCollector(\u003cstatsd addr\u003e, \u003cmetrics-prefix\u003e)` option when initializing the client as shown above.\n\n### Creating a hystrix-like circuit breaker with fallbacks\n\nYou can use the `hystrix.NewClient` function to create a client wrapped in a hystrix-like circuit breaker by passing in your own custom fallbacks:\n\nThe fallback function will trigger when your code returns an error, or whenever it is unable to complete based on a variety of [health checks](https://github.com/Netflix/Hystrix/wiki/How-it-Works).\n\n**How your fallback function should look like**\nyou should pass in a function whose signature looks like following\n\n```go\nfunc(err error) error {\n    // your logic for handling the error/outage condition\n    return err\n}\n```\n\n\n**Example**\n\n```go\n// Create a new fallback function\nfallbackFn := func(err error) error {\n    _, err := http.Post(\"post_to_channel_two\")\n    return err\n}\n\ntimeout := 10 * time.Millisecond\n\n// Create a new hystrix-wrapped HTTP client with the fallbackFunc as fall-back function\nclient := hystrix.NewClient(\n\thystrix.WithHTTPTimeout(timeout),\n\thystrix.WithCommandName(\"MyCommand\"),\n\thystrix.WithHystrixTimeout(1100 * time.Millisecond),\n\thystrix.WithMaxConcurrentRequests(100),\n\thystrix.WithErrorPercentThreshold(20),\n\thystrix.WithSleepWindow(10),\n\thystrix.WithRequestVolumeThreshold(10),\n\thystrix.WithFallbackFunc(fallbackFn),\n})\n\n// The rest is the same as the previous example\n```\n\nIn the above example, the `fallbackFunc` is a function which posts to channel two in case posting to channel one fails.\n\n### Creating an HTTP client with a retry mechanism\n\n```go\n// First set a backoff mechanism. Constant backoff increases the backoff at a constant rate\nbackoffInterval := 2 * time.Millisecond\n// Define a maximum jitter interval. It must be more than 1*time.Millisecond\nmaximumJitterInterval := 5 * time.Millisecond\n\nbackoff := heimdall.NewConstantBackoff(backoffInterval, maximumJitterInterval)\n\n// Create a new retry mechanism with the backoff\nretrier := heimdall.NewRetrier(backoff)\n\ntimeout := 1000 * time.Millisecond\n// Create a new client, sets the retry mechanism, and the number of times you would like to retry\nclient := httpclient.NewClient(\n\thttpclient.WithHTTPTimeout(timeout),\n\thttpclient.WithRetrier(retrier),\n\thttpclient.WithRetryCount(4),\n)\n\n// The rest is the same as the first example\n```\nOr create client with exponential backoff\n\n```go\n// First set a backoff mechanism. Exponential Backoff increases the backoff at a exponential rate\n\ninitalTimeout := 2*time.Millisecond            // Inital timeout\nmaxTimeout := 9*time.Millisecond               // Max time out\nexponentFactor := 2                            // Multiplier\nmaximumJitterInterval := 2*time.Millisecond    // Max jitter interval. It must be more than 1*time.Millisecond\n\nbackoff := heimdall.NewExponentialBackoff(initalTimeout, maxTimeout, exponentFactor, maximumJitterInterval)\n\n// Create a new retry mechanism with the backoff\nretrier := heimdall.NewRetrier(backoff)\n\ntimeout := 1000 * time.Millisecond\n// Create a new client, sets the retry mechanism, and the number of times you would like to retry\nclient := httpclient.NewClient(\n\thttpclient.WithHTTPTimeout(timeout),\n\thttpclient.WithRetrier(retrier),\n\thttpclient.WithRetryCount(4),\n)\n\n// The rest is the same as the first example\n```\n\nThis will create an HTTP client which will retry every `500` milliseconds incase the request fails. The library also comes with an [Exponential Backoff](https://pkg.go.dev/github.com/gojek/heimdall#NewExponentialBackoff)\n\n### Custom retry mechanisms\n\nHeimdall supports custom retry strategies. To do this, you will have to implement the `Backoff` interface:\n\n```go\ntype Backoff interface {\n\tNext(retry int) time.Duration\n}\n```\n\nLet's see an example of creating a client with a linearly increasing backoff time:\n\nFirst, create the backoff mechanism:\n\n```go\ntype linearBackoff struct {\n\tbackoffInterval int\n}\n\nfunc (lb *linearBackoff) Next(retry int) time.Duration{\n\tif retry \u003c= 0 {\n\t\treturn 0 * time.Millisecond\n\t}\n\treturn time.Duration(retry * lb.backoffInterval) * time.Millisecond\n}\n```\n\nThis will create a backoff mechanism, where the retry time will increase linearly for each retry attempt. We can use this to create the client, just like the last example:\n\n```go\nbackoff := \u0026linearBackoff{100}\nretrier := heimdall.NewRetrier(backoff)\n\ntimeout := 1000 * time.Millisecond\n// Create a new client, sets the retry mechanism, and the number of times you would like to retry\nclient := httpclient.NewClient(\n\thttpclient.WithHTTPTimeout(timeout),\n\thttpclient.WithRetrier(retrier),\n\thttpclient.WithRetryCount(4),\n)\n\n// The rest is the same as the first example\n```\n\nHeimdall also allows you to simply pass a function that returns the retry timeout. This can be used to create the client, like:\n```go\nlinearRetrier := NewRetrierFunc(func(retry int) time.Duration {\n\tif retry \u003c= 0 {\n\t\treturn 0 * time.Millisecond\n\t}\n\treturn time.Duration(retry) * time.Millisecond\n})\n\ntimeout := 1000 * time.Millisecond\nclient := httpclient.NewClient(\n\thttpclient.WithHTTPTimeout(timeout),\n\thttpclient.WithRetrier(linearRetrier),\n\thttpclient.WithRetryCount(4),\n)\n```\n\n### Custom HTTP clients\n\nHeimdall supports custom HTTP clients. This is useful if you are using a client imported from another library and/or wish to implement custom logging, cookies, headers etc for each request that you make with your client.\n\nUnder the hood, the `httpClient` struct now accepts `Doer`, which is the standard interface implemented by HTTP clients (including the standard library's `net/*http.Client`)\n\nLet's say we wish to add authorization headers to all our requests.\n\nWe can define our client `myHTTPClient`\n\n```go\ntype myHTTPClient struct {\n\tclient http.Client\n}\n\nfunc (c *myHTTPClient) Do(request *http.Request) (*http.Response, error) {\n\trequest.SetBasicAuth(\"username\", \"passwd\")\n\treturn c.client.Do(request)\n}\n```\n\nAnd set this with `httpclient.NewClient(httpclient.WithHTTPClient(\u0026myHTTPClient{client: http.DefaultClient}))`\n\nNow, each sent request will have the `Authorization` header to use HTTP basic authentication with the provided username and password.\n\nThis can be done for the hystrix client as well\n\n```go\nclient := httpclient.NewClient(\n\thttpclient.WithHTTPClient(\u0026myHTTPClient{\n\t\tclient: http.Client{Timeout: 25 * time.Millisecond},\n\t}),\n)\n\n// The rest is the same as the first example\n```\n\n## Plugins\n\nTo add a plugin to an existing client, use the `AddPlugin` method of the client. \n\nAn example, with the [request logger plugin](/plugins/request_logger.go):\n\n```go\n// import \"github.com/gojek/heimdall/v7/plugins\"\n\nclient := heimdall.NewHTTPClient(timeout)\nrequestLogger := plugins.NewRequestLogger(nil, nil)\nclient.AddPlugin(requestLogger)\n// use the client as before\n\nreq, _ := http.NewRequest(http.MethodGet, \"http://google.com\", nil)\nres, err := client.Do(req)\nif err != nil {\n\tpanic(err)\n}\n// This will log:\n//23/Jun/2018 12:48:04 GET http://google.com 200 [412ms]\n// to STDOUT\n```\n\nA plugin is an interface whose methods get called during key events in a requests lifecycle:\n\n- `OnRequestStart` is called just before the request is made\n- `OnRequestEnd` is called once the request has successfully executed\n- `OnError` is called is the request failed\n\nEach method is called with the request object as an argument, with `OnRequestEnd`, and `OnError` additionally being called with the response and error instances respectively.\nFor a simple example on how to write plugins, look at the [request logger plugin](/plugins/request_logger.go).\n\n## Documentation\n\nFurther documentation can be found on [pkg.go.dev](https://pkg.go.dev/github.com/gojek/heimdall/v7)\n\n## FAQ\n\n**Can I replace the standard Go HTTP client with Heimdall?**\n\nYes, you can. Heimdall implements the standard [HTTP Do](https://golang.org/pkg/net/http/#Client.Do) method, along with [useful wrapper methods](https://golang.org/pkg/net/http/#Client.Do) that provide all the functionality that a regular Go HTTP client provides.\n\n---\n\n**When should I use Heimdall?**\n\nIf you are making a large number of HTTP requests, or if you make requests among multiple distributed nodes, and wish to make your systems more fault tolerant, then Heimdall was made for you.\n\nHeimdall makes use of [multiple mechanisms](https://medium.com/@sohamkamani/how-to-handle-microservice-communication-at-scale-a6fb0ee0ed7) to make HTTP requests more fault tolerant:\n1. Retries - If a request fails, Heimdall retries behind the scenes, and returns the result if one of the retries are successful.\n2. Circuit breaking - If Heimdall detects that too many of your requests are failing, or that the number of requests sent are above a configured threshold, then it \"opens the circuit\" for a short period of time, which prevents any more requests from being made. _This gives your downstream systems time to recover._\n\n---\n\n**So does this mean that I shouldn't use Heimdall for small scale applications?**\n\nAlthough Heimdall was made keeping large scale systems in mind, it's interface is simple enough to be used for any type of systems. In fact, we use it for our pet projects as well. Even if you don't require retries or circuit breaking features, the [simpler HTTP client](https://github.com/gojek/heimdall#making-a-simple-get-request) provides sensible defaults with a simpler interface, and can be upgraded easily should the need arise.\n\n---\n\n**Can I contribute to make Heimdall better?**\n\n[Please do!](https://github.com/gojek/heimdall/blob/master/CONTRIBUTING.md) We are looking for any kind of contribution to improve Heimdalls core funtionality and documentation. When in doubt, make a PR!\n\n## License\n\n```\nCopyright 2018-2020, GO-JEK Tech (http://gojek.tech)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgojek%2Fheimdall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgojek%2Fheimdall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgojek%2Fheimdall/lists"}