{"id":21561868,"url":"https://github.com/0x9ef/clientx","last_synced_at":"2025-04-10T12:06:31.193Z","repository":{"id":223882048,"uuid":"761712938","full_name":"0x9ef/clientx","owner":"0x9ef","description":"Library for fast building client API. Uses generics to encode/decode custom models","archived":false,"fork":false,"pushed_at":"2024-07-18T11:01:34.000Z","size":58,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T10:56:26.957Z","etag":null,"topics":["api","api-design","client","golang","library","rate-limiting","retry-library"],"latest_commit_sha":null,"homepage":"","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/0x9ef.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}},"created_at":"2024-02-22T10:52:25.000Z","updated_at":"2024-07-18T11:00:57.000Z","dependencies_parsed_at":"2024-07-18T13:04:43.773Z","dependency_job_id":null,"html_url":"https://github.com/0x9ef/clientx","commit_stats":null,"previous_names":["0x9ef/clientx"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x9ef%2Fclientx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x9ef%2Fclientx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x9ef%2Fclientx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x9ef%2Fclientx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0x9ef","download_url":"https://codeload.github.com/0x9ef/clientx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248217009,"owners_count":21066633,"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":["api","api-design","client","golang","library","rate-limiting","retry-library"],"created_at":"2024-11-24T09:28:48.890Z","updated_at":"2025-04-10T12:06:31.176Z","avatar_url":"https://github.com/0x9ef.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Golang client for API building\nThe purpose of this client is to design and develop clients for any API very fast using generics for request, response models encoding/decoding with supported from the box retry, rate-limit, GZIP/Deflate decoding functionality.\n\n## Installation\n\u003e NOTE: Requires at least Go 1.18 since we use generics\n\nTo get latest version use:\n```\ngo get github.com/0x9ef/clientx@latest\n```\n\nTo specify version use:\n```\ngo get github.com/0x9ef/clientx@1.24.4 # version\n```\n\n## Usage examples\n* Check out the ready-to-use [HotelBeds Client](https://github.com/0x9ef/hotelbeds-go) built with ClientX\n* See the [examples/](https://github.com/0x9ef/clientx/blob/master/examples) folder (CatFacts, PHPNoise APIs)\n\n## Getting Started\nThe first thing you need to understand: it will be easy :)\n\nThe client was developed with consuming needs of modern API development. I have designed and integrated many clients for different APIs and came up with a couple useful things. I was intended to make it easy, understandable even for beginner, and include top of the most necessary functionality.\n\n### Initialize\nWhen you are initializing client, you MUST specify base URL by clientx.WithBaseURL option.\n\n```go\napi := clientx.NewAPI(\n\tclientx.WithBaseURL(\"https://php-noise.com\"),\n}\n```\n\n### Authorizarion\nThere is no separate flow for authorization, but it can be done with HTTP headers. Let's talk about Bearer authorization. You have the API Access Token and you have to build HTTP Header: `Authorizarion: Bearer MY_ACCESS_TOKEN`, you could pass it through request options.\n\n```go\n// GetOffer accepts offerId and request options that will be applied before request is sent.\nfunc (api *MyAPI) GetOffer(ctx context.Context, offerId string, opts ...clientx.RequestOption) (*Offer, error) {\n\treturn clientx.NewRequestBuilder[struct{}, Offer](api.API).\n\t\tGet(\"/offers/\"+offerId, opts...).\n\t\tDoWithDecode(ctx)\n}\n\nfunc main() {\n\t... \n\tctx, cancel := context.WithTimeout(context.Background(), time.Minute)\n\tdefer cancel()\n\t\n\tresp, err := api.GetOffer(ctx, \"off_1234\", clientx.WithRequestHeaders(map[string][]string{\n\t\t\"Authorization\": []string{\"Bearer MY_ACCESS_TOKEN\"}, \n\t}))\n}\n```\n\n### Options\nThere is a list of supported options from the box:\n* WithRateLimit - enables rate limiting mechanism\n* WithRetry - enables backoff retry mechanism\n\n```go\napi := New(\n\tclientx.NewAPI(\n\t\tclientx.WithBaseURL(\"https://php-noise.com\"),\n\t\tclientx.WithRateLimit(10, 2, time.Minute),\n\t\tclientx.WithRetry(10, time.Second*3, time.Minute, clientx.ExponentalBackoff,\n\t\t\tfunc(resp *http.Response, err error) bool {\n\t\t\t\treturn resp.StatusCode == http.StatusTooManyRequests\n\t\t\t},\n\t\t),\n\t),\n)\n```\n\n### Rate limiting\nMost of the APIs have rate limits. Let's take for example the next limit: 100req/sec, so we want to stay within the limit. The rate limiter functionality supported from the box: wrapper around golang.org/x/time/rate package.\n\n```go\napi := New(\n\tclientx.NewAPI(\n\t\tclientx.WithBaseURL(\"https://php-noise.com\"),\n\t\tclientx.WithRateLimit(10, 2, time.Minute), // max limit: ^10, burst limit: ^2, interval: ^time.Minute\n\t),\n)\n``` \n\nIf the limit is exceeded, all further call will be blocked until we gain enough capacity to perform new requests.\n\n### Retry\nRetry functionality can be combined with rate limiting. There are cases when you don't know the rate limiting interval. In this case you can use backoff retry mechanism. You can retry after 1 sec or you can wait for 60 minutes. The 429 (Too Many Requests) status code is an indicator when rate limit is exceeded.\n\n```go\napi := New(\n\tclientx.NewAPI(\n\t\tclientx.WithBaseURL(\"https://php-noise.com\"),\n\t\tclientx.WithRateLimit(10, 2, time.Minute), \n\t\t// Parameters: max retry attempts, minimal wait time, maximal wait time, retry function (you could provide your own which is suitable for clientx.RetryFunc), trigger function (in our example we consider all 429 statuses as a tigger)\n\t\tclientx.WithRetry(10, time.Second*3, time.Minute, clientx.ExponentalBackoff,\n\t\t\tfunc(resp *http.Response, err error) bool {\n\t\t\t\treturn resp.StatusCode == http.StatusTooManyRequests\n\t\t\t},\n\t\t),\n\t),\n)\n```\n\n### Request options\nYou can add custom headers to request or set query parameters, form data, etc... The list of supported request options you can find [here](https://github.com/0x9ef/clientx/blob/master/requestoptions.go).\n\n```go\nfunc (api *MyAPI) GetOffer(ctx context.Context, offerId string, opts ...clientx.RequestOption) (*Offer, error) {\n\treturn clientx.NewRequestBuilder[struct{}, Offer](api.API).\n\t\tGet(\"/offers/\"+offerId, opts...).\n\t\tDoWithDecode(ctx)\n}\n\nfunc main() {\n    ... \n\tctx, cancel := context.WithTimeout(context.Background(), time.Minute)\n\tdefer cancel()\n\n\tresp, err := api.GetOffer(ctx, \"off_1234\", clientx.WithRequestHeaders(map[string][]string{\n\t\t\"Authorization\":    []string{\"Bearer token_test\"}, \n\t\t\"X-Correlation-Id\": []string{\"mdj34fjhgsdb4\"},\n\t}))\n}\n```\n\n### Query parameters encode\nThere are two ways to encode query parameters, one can be preferred rather than another one.\n\n```go\ntype GetOfferParams struct {\n\tFilterBy string `url:\"filter_by\"`\n}\n\nfunc (param GetOfferParam) Encode(v url.Values) error {\n\tv.Set(\"filter_by\", param.FilterBy)\n\treturn nil\n}\n\n\n// Variant based on WithQueryParams (when we want to encode through structure tags) \nfunc (api *MyAPI) GetOffer(ctx context.Context, offerId string, params GetOfferParams, opts ...clientx.RequestOption) (*Offer, error) {\n\treturn clientx.NewRequestBuilder[struct{}, Offer](api.API).\n\t\tGet(\"/offers/\"+offerId, opts...).\n\t\tWithQueryParams(\"url\", params).\n\t\tDoWithDecode(ctx)\n}\n\n// Variant based on WithEncodableQueryParams when we implement clientx.ParamEncoder interface\nfunc (api *MyAPI) GetOffer(ctx context.Context, offerId string, params GetOfferParams, opts ...clientx.RequestOption) (*Offer, error) {\n\treturn clientx.NewRequestBuilder[struct{}, Offer](api.API).\n\t\tGet(\"/offers/\"+offerId, opts...).\n\t\tWithEncodableQueryParams(params).\n\t\tDoWithDecode(ctx)\n}\n```\n\n### Custom encoding \u0026 decoding \nBy default, ClientX uses JSON encoder if not specified. If you want to encode/decode payload and responses in XML or any other formats, you should implement `clientx.EncoderDecoder` and pass it as a second argument into `DoWithDecode` function.\n\n```go\nfunc (api *MyAPI) CreateOffer(ctx context.Context, offerId string, body GetOfferParams, opts ...clientx.RequestOption) (*Offer, error) {\n\treturn clientx.NewRequestBuilder[struct{}, Offer](api.API).\n\t\tPost(\"/offers/\"+offerId, \u0026body, opts...).\n \t\tWithEncodableQueryParams(params).\n\t\tDoWithDecode(ctx, clientx.XMLEncoderDecoder) // selected XML encoder\n}\n```\n\n**Encoders supported from the box**:\n- JSON\n- XML\n- Blank (No actions, no errors)\n\n## Contributing\nIf you found a bug or have an idea for a new feature, please first discuss it with us by [submitting a new issue](https://github.com/0x9ef/clientx/issues). \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x9ef%2Fclientx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0x9ef%2Fclientx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x9ef%2Fclientx/lists"}