Coder Social home page Coder Social logo

0x9ef / clientx Goto Github PK

View Code? Open in Web Editor NEW
8.0 1.0 1.0 58 KB

Library for fast building client API. Uses generics to encode/decode custom models

License: MIT License

Go 100.00%
api api-design client golang library rate-limiting retry-library

clientx's Introduction

Golang client for API building

The 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.

Installation

NOTE: Requires at least Go 1.18 since we use generics

To get latest version use:

go get github.com/0x9ef/clientx@latest

To specify version use:

go get github.com/0x9ef/[email protected] # version

Usage examples

Getting Started

The first thing you need to understand: it will be easy :)

The 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.

Initialize

When you are initializing client, you MUST specify base URL by clientx.WithBaseURL option.

api := clientx.NewAPI(
	clientx.WithBaseURL("https://php-noise.com"),
}

Authorizarion

There 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.

// GetOffer accepts offerId and request options that will be applied before request is sent.
func (api *MyAPI) GetOffer(ctx context.Context, offerId string, opts ...clientx.RequestOption) (*Offer, error) {
	return clientx.NewRequestBuilder[struct{}, Offer](api.API).
		Get("/offers/"+offerId, opts...).
		DoWithDecode(ctx)
}

func main() {
	... 
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	
	resp, err := api.GetOffer(ctx, "off_1234", clientx.WithRequestHeaders(map[string][]string{
		"Authorization": []string{"Bearer MY_ACCESS_TOKEN"}, 
	}))
}

Options

There is a list of supported options from the box:

  • WithRateLimit - enables rate limiting mechanism
  • WithRetry - enables backoff retry mechanism
api := New(
	clientx.NewAPI(
		clientx.WithBaseURL("https://php-noise.com"),
		clientx.WithRateLimit(10, 2, time.Minute),
		clientx.WithRetry(10, time.Second*3, time.Minute, clientx.ExponentalBackoff,
			func(resp *http.Response, err error) bool {
				return resp.StatusCode == http.StatusTooManyRequests
			},
		),
	),
)

Rate limiting

Most 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.

api := New(
	clientx.NewAPI(
		clientx.WithBaseURL("https://php-noise.com"),
		clientx.WithRateLimit(10, 2, time.Minute), // max limit: ^10, burst limit: ^2, interval: ^time.Minute
	),
)

If the limit is exceeded, all further call will be blocked until we gain enough capacity to perform new requests.

Retry

Retry 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.

api := New(
	clientx.NewAPI(
		clientx.WithBaseURL("https://php-noise.com"),
		clientx.WithRateLimit(10, 2, time.Minute), 
		// 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)
		clientx.WithRetry(10, time.Second*3, time.Minute, clientx.ExponentalBackoff,
			func(resp *http.Response, err error) bool {
				return resp.StatusCode == http.StatusTooManyRequests
			},
		),
	),
)

Request options

You can add custom headers to request or set query parameters, form data, etc... The list of supported request options you can find here.

func (api *MyAPI) GetOffer(ctx context.Context, offerId string, opts ...clientx.RequestOption) (*Offer, error) {
	return clientx.NewRequestBuilder[struct{}, Offer](api.API).
		Get("/offers/"+offerId, opts...).
		DoWithDecode(ctx)
}

func main() {
    ... 
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	resp, err := api.GetOffer(ctx, "off_1234", clientx.WithRequestHeaders(map[string][]string{
		"Authorization":    []string{"Bearer token_test"}, 
		"X-Correlation-Id": []string{"mdj34fjhgsdb4"},
	}))
}

Query parameters encode

There are two ways to encode query parameters, one can be preferred rather than another one.

type GetOfferParams struct {
	FilterBy string `url:"filter_by"`
}

func (param GetOfferParam) Encode(v url.Values) error {
	v.Set("filter_by", param.FilterBy)
	return nil
}


// Variant based on WithQueryParams (when we want to encode through structure tags) 
func (api *MyAPI) GetOffer(ctx context.Context, offerId string, params GetOfferParams, opts ...clientx.RequestOption) (*Offer, error) {
	return clientx.NewRequestBuilder[struct{}, Offer](api.API).
		Get("/offers/"+offerId, opts...).
		WithQueryParams("url", params).
		DoWithDecode(ctx)
}

// Variant based on WithEncodableQueryParams when we implement clientx.ParamEncoder interface
func (api *MyAPI) GetOffer(ctx context.Context, offerId string, params GetOfferParams, opts ...clientx.RequestOption) (*Offer, error) {
	return clientx.NewRequestBuilder[struct{}, Offer](api.API).
		Get("/offers/"+offerId, opts...).
		WithEncodableQueryParams(params).
		DoWithDecode(ctx)
}

Custom encoding & decoding

By 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.

func (api *MyAPI) CreateOffer(ctx context.Context, offerId string, body GetOfferParams, opts ...clientx.RequestOption) (*Offer, error) {
	return clientx.NewRequestBuilder[struct{}, Offer](api.API).
		Post("/offers/"+offerId, &body, opts...).
 		WithEncodableQueryParams(params).
		DoWithDecode(ctx, clientx.XMLEncoderDecoder) // selected XML encoder
}

Encoders supported from the box:

  • JSON
  • XML
  • Blank (No actions, no errors)

Contributing

If you found a bug or have an idea for a new feature, please first discuss it with us by submitting a new issue.

clientx's People

Contributors

0x9ef avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

uxrust

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.