Skip to content
/ hx Public

๐ŸŒ Developer-friendly, Real-World-ready and extensible HTTP client for Go

License

Notifications You must be signed in to change notification settings

izumin5210/hx

Repository files navigation

hx

CI GoDoc codecov License

Developer-friendly, Production-ready and extensible HTTP client for Go

Features

...

Plugins

  • hxlog - Logging requests and responses with standard logger
  • hxlog - Logging requests and responses with zap
  • pb - Marshaling and Unmarshaling protocol buffers
  • retry - Retrying HTTP requests

Examples

Simple GET

type Content struct {
	Body string `json:"body"`
}

var cont Content

ctx := context.Background()
err := hx.Get(ctx, "https://api.example.com/contents/1",
	hx.WhenSuccess(hx.AsJSON(&cont)),
	hx.WhenFailure(hx.AsError()),
)

Real-world

func init() {
	defaultTransport := hxutil.CloneTransport(http.DefaultTransport.(*http.Transport))

	// Tweak keep-alive configuration
	defaultTransport.MaxIdleConns = 500
	defaultTransport.MaxIdleConnsPerHost = 100

	// Set global options
	hx.DefaultOptions = append(
		hx.DefaultOptions,
		hx.UserAgent(fmt.Sprintf("yourapp (%s)", hx.DefaultUserAgent)),
		hx.Transport(defaultTransport),
		hx.TransportFrom(func(rt http.RoundTripper) http.RoundTripper {
			return &ochttp.Transport{Base: rt}
		}),
	)
}

func NewContentAPI() *hx.Client {
	// Set common options for API ciient
	return &ContentAPI{
		client: hx.NewClient(
			hx.BaseURL("https://api.example.com"),
		),
	}
}

type ContentAPI struct {
	client *hx.Client
}

func (a *ContentAPI) GetContent(ctx context.Context, id int) (*Content, error) {
	var cont Content

	err := a.client.Get(ctx, hx.Path("api", "contents", id),
		hx.WhenSuccess(hx.AsJSON(&cont)),
		hx.WhenFailure(hx.AsError()),
	)

	if err != nil {
		// ...
	}

	return &cont, nil
}

func (a *ContentAPI) CreateContent(ctx context.Context, in *Content) (*Content, error) {
	var out Content

	err := a.client.Post(ctx, "/api/contents",
		hx.JSON(in),
		hx.WhenSuccess(hx.AsJSON(&out)),
		hx.WhenStatus(hx.AsJSONError(&InvalidArgument{}), http.StatusBadRequest),
		hx.WhenFailure(hx.AsError()),
	)

	if err != nil {
		var (
			invalidArgErr *InvalidArgument
			respErr       *hx.ResponseError
		)
		if errors.As(err, &invalidArgErr) {
			// handle known error
		} else if errors.As(err, &respErr) {
			// handle unknown response error
		} else {
			err := errors.Unwrap(err)
			// handle unknown error
		}
	}

	return &out, nil
}