/roku

The Roku REST client provides a straightforward and simple interface for interacting with REST APIs.

Primary LanguageGoMIT LicenseMIT

Roku

Overview

The Roku REST client provides a straightforward and simple interface for interacting with REST APIs. With Roku you can:

  • Easily marshal and unmarshal requests and responses without the need for boilerplate or repetitive code.
  • Utilize the built-in exponential backoff algorithm.
  • Enhance your request functions by wrapping them with RxGo 2 Observable streams, enabling the creation of custom pipelines, including parallel requests.

Table of Contents

Usage.

  • Before starting to make requests, we need to create a *http.Client in Golang. For this, we can use Roku's constructor method:

    import (
      ...
      "github.com/v8tix/roku"
      "net/http"
      ...
    )
    
    func NewHTTPClient(
      timeout time.Duration,
      redirectPolicy func(req *http.Request, via []*http.Request) error,
      transport http.RoundTripper,
    ) *http.Client {
    ...
    }
    
    • Where:

      timeout: Its purpose is to control how long the client waits to receive a response from the server before considering the request as failed due to a timeout.

      redirectPolicy: Its purpose is to allow you to control how the HTTP client handles 3xx redirection responses.

      transport: Its purpose is to allow you to have more granular control over how HTTP requests are made and how network connections are managed.

    • Example:

    httpClient := roku.NewHTTPClient(
    	5*time.Second,
    	policy.OneRedirect,
    	transport.IdleConnectionTimeout(15*time.Second),
    )
    


    The OneRedirect and IdleConnectionTimeout functions are examples of how the Golang REST client can be configured and are part of Roku.

*Once our client is configured, we can start configuring the functions provided by Roku: Fetch and FetchRx. The difference between them is that Fetch does not provide a backoff mechanism and does not return the response as an observable. For production environments, we recommend using Rx.

  • FetchRx signature:
func FetchRx[T ReqI, U ResI](
  ctx context.Context,
  client *http.Client,
  method HTTPMethod,
  endpoint string,
  request *T,
  headers map[string]string,
  deadline time.Duration,
  backoffInterval time.Duration,
  backoffRetries uint64,
  statusCodeValidator ...func(res *http.Response) bool,
) rxgo.Observable {
  ...
}
  • FetchRx example:
  ch := roku.FetchRx[CreateUserV1Req, GetUserEnvV1Res](
          context.Background(),
          httpClient,
          roku.Put,
          url,
          createUserV1Req,
          nil,
          time.Second,
          150 * time.Millisecond,
          3,
  ).Observe()

  res, err := roku.To[roku.Envelope[GetUserEnvV1Res]](<-ch)
  switch err {
    case nil:
      if res.Body != nil {
        ...  
        return res.Body.User
      }
    default:
        var errHTTP roku.ErrInvalidHTTPStatus

        if errors.As(err, &errHTTP) {
            errDesc := roku.GetErrorDesc(errHTTP)
            return fmt.Errorf("http error: %v", errDesc)      
        }
  }
  • Notes about the example:
    • Due to generics in Golang, CreateUserV1Req and GetUserEnvV1Res must implement the roku.ReqI and roku.ResI interfaces, respectively.
  type CreateUserV1Req struct {
		Name  string `json:"name,omitempty"`
		Email string `json:"email,omitempty"`
  }
  
  func (CreateUserV1Req) Req() {}
  
  type GetUserEnvV1Res struct {
    User GetUserV1Res `json:"user,omitempty"`
  }
  
  func (GetUserEnvV1Res) Res() {}
  • In cases where the request does not have a request body, you should use the type roku.NoReq and pass nil as a parameter to FetchRx.
  • In cases where the request does not return a response, you should use the type roku.NoRes for FetchRx.
  ch:= roku.FetchRx[roku.NoReq, roku.NoRes](
    ctx,
    httpClient,
    roku.Delete,
    url,
    nil,
    nil,
    roku.DeadLine,
    roku.RetryInterval,
    3,
  ).Observe()

  _, err := roku.To[roku.Envelope[roku.NoRes]](<-ch)
  switch err {
    case nil:
      return nil
    default:
        var errHTTP roku.ErrInvalidHTTPStatus

        if errors.As(err, &errHTTP) {
            errDesc := roku.GetErrorDesc(errHTTP)
            return fmt.Errorf("http error: %v", errDesc)      
        }
  }
  • deadline: It enables FetchRx to block requests to the service, preventing both overloading and cascading failures.

  • backoffInterval: It represents a waiting period in which FetchRx delays before attempting to retry a failed request.

  • backoffRetries: It represents the maximum number of retry attempts that FetchRx will make for a request.

Contributing.

  1. Fork the repository
  2. Clone the forked repository
  3. Create a new branch for your feature or fix
  4. Make your changes
  5. Submit a pull request

License.

This project is licensed under the MIT License. You can refer to the LICENSE file for more information.