diff --git a/README.md b/README.md index 698aef9..708db13 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,45 @@ # httpclient -`httpclient` is a lightweight and efficient HTTP client library written in Go. It simplifies making HTTP requests by providing an easy-to-use interface for GET, POST, PUT, DELETE, and other HTTP methods. This library is designed to handle common use cases like setting headers, query parameters, and handling JSON payloads. +A modern, lightweight, and extensible HTTP client library for Go, designed to make HTTP requests simple, reliable, and powerful. `httpclient` wraps Go's standard `http.Client` and adds advanced features such as retries, exponential backoff, custom logging, default headers, and more, all with an easy-to-use API. + +--- + +## Features + +- **Simple API**: Clean, intuitive methods for GET, POST, PUT, DELETE, and JSON requests. +- **Retries & Exponential Backoff**: Automatically retry failed requests with optional exponential backoff. +- **Custom Logging**: Plug in your own logger for detailed request/response logs. +- **Default Headers**: Set headers to be included in every request. +- **Custom Transport & TLS**: Use your own `http.RoundTripper` or TLS configuration. +- **Timeouts**: Easily configure request timeouts. +- **Response Helpers**: Utility functions to read and unmarshal response bodies. +- **Well-tested**: Comprehensive unit tests for reliability. + +--- + +## Architecture + +The core of `httpclient` is the `HTTPClient` struct, which wraps Go's `http.Client` and is configured using functional options. Helper methods provide a simple interface for common HTTP operations, while advanced features like retries and logging are built-in. + +```mermaid +flowchart TD + A["User Code"] -->|"calls"| B["httpclient.NewHTTPClient()"] + B -->|"returns"| C["HTTPClient struct"] + C -->|"calls"| D["http.Client"] + C -->|"uses"| E["Options (WithRetry, WithTimeout, etc.)"] + C -->|"calls"| F["Do() method"] + F -->|"calls"| G["http.Client.Do()"] + F -->|"handles"| H["Retry, Logging, Backoff"] + C -->|"provides"| I["Helpers: Get, Post, Put, Delete, PostJSON"] + I -->|"wraps"| F + C -->|"uses"| J["headerTransport (default headers)"] + F -->|"returns"| K["http.Response"] + K -->|"used by"| L["ReadResponseBody, ReadJSONResponseBody"] +``` -## Installation +--- -To use `httpclient` in your project, you can install it using `go get`: +## Installation ```bash go get github.com/chinnareddy578/httpclient @@ -12,66 +47,111 @@ go get github.com/chinnareddy578/httpclient ## Importing -After installation, you can import the library into your Go project: - ```go import "github.com/chinnareddy578/httpclient" ``` +--- + ## Usage -Here is an example of how to use `httpclient`: +### Creating a Client ```go -package main - -import ( - "fmt" - "github.com/chinnareddy578/httpclient" +client := httpclient.NewHTTPClient( + httpclient.WithTimeout(5 * time.Second), + httpclient.WithRetry(3, 1 * time.Second), + httpclient.WithLogger(log.Default()), + httpclient.WithDefaultHeaders(map[string]string{"X-App": "myapp"}), ) +``` + +### GET Request -func main() { - client := httpclient.New() - - // Example GET request with headers - headers := map[string]string{ - "Authorization": "Bearer your-token", - } - response, err := client.Get("https://api.example.com/data", headers) - if err != nil { - fmt.Println("Error:", err) - return - } - fmt.Println("Response:", string(response.Body)) - - // Example POST request with JSON payload and headers - payload := map[string]interface{}{ - "name": "John Doe", - "age": 30, - } - response, err = client.Post("https://api.example.com/users", payload, headers) - if err != nil { - fmt.Println("Error:", err) - return - } - fmt.Println("Response:", string(response.Body)) - - // Example PUT request - updatePayload := map[string]interface{}{ - "age": 31, - } - response, err = client.Put("https://api.example.com/users/1", updatePayload, headers) - if err != nil { - fmt.Println("Error:", err) - return - } - fmt.Println("Response:", string(response.Body)) +```go +headers := map[string]string{"Authorization": "Bearer token"} +resp, err := client.Get("https://api.example.com/data", headers) +if err != nil { + log.Fatal(err) } +body, _ := httpclient.ReadResponseBody(resp) +fmt.Println(body) ``` -## Contribution Guide +### POST Request (Raw Body) + +```go +body := bytes.NewBufferString("raw payload") +headers := map[string]string{"Content-Type": "text/plain"} +resp, err := client.Post("https://api.example.com/upload", body, headers) +if err != nil { + log.Fatal(err) +} +bodyStr, _ := httpclient.ReadResponseBody(resp) +fmt.Println(bodyStr) +``` + +### POST Request (JSON) + +```go +payload := map[string]interface{}{"name": "John", "age": 30} +headers := map[string]string{"Authorization": "Bearer token"} +resp, err := client.PostJSON("https://api.example.com/users", payload, headers) +if err != nil { + log.Fatal(err) +} +var result map[string]interface{} +_ = httpclient.ReadJSONResponseBody(resp, &result) +fmt.Println(result) +``` + +### PUT Request + +```go +update := map[string]interface{}{"age": 31} +updateBody, _ := json.Marshal(update) +headers := map[string]string{"Content-Type": "application/json"} +resp, err := client.Put("https://api.example.com/users/1", bytes.NewReader(updateBody), headers) +if err != nil { + log.Fatal(err) +} +bodyStr, _ := httpclient.ReadResponseBody(resp) +fmt.Println(bodyStr) +``` + +### DELETE Request + +```go +headers := map[string]string{"Authorization": "Bearer token"} +resp, err := client.Delete("https://api.example.com/users/1", headers) +if err != nil { + log.Fatal(err) +} +bodyStr, _ := httpclient.ReadResponseBody(resp) +fmt.Println(bodyStr) +``` + +--- + +## Advanced Features & Options + +- **WithRetry(retryCount, retryDelay)**: Set the number of retries and delay between retries for failed requests. +- **WithExponentialBackoff(baseDelay)**: Enable exponential backoff for retries. +- **WithLogger(logger)**: Use a custom logger for request/response logging. +- **WithTransport(transport)**: Use a custom `http.RoundTripper` for advanced networking. +- **WithTLSConfig(tlsConfig)**: Set a custom TLS configuration. +- **WithDefaultHeaders(headers)**: Set default headers for all requests. +- **WithTimeout(timeout)**: Set a timeout for all requests. -We welcome contributions to improve `httpclient`. To contribute, please follow these steps: +--- + +## Submitting Suggestions and Issues + +We welcome suggestions, bug reports, and feature requests! Please use the [GitHub Issues page](https://github.com/chinnareddy578/httpclient/issues) to submit your feedback. + +--- + +## Contribution Guide 1. Fork the repository on GitHub. 2. Create a new branch for your feature or bug fix: @@ -86,11 +166,19 @@ We welcome contributions to improve `httpclient`. To contribute, please follow t 5. Open a pull request on the main repository. ### Guidelines - - Ensure your code follows Go best practices. - Write tests for any new features or bug fixes. - Update the documentation if necessary. +--- + +## References + +- [GitHub Repository](https://github.com/chinnareddy578/httpclient) +- [GoDoc Documentation](https://pkg.go.dev/github.com/chinnareddy578/httpclient) + +--- + ## License This project is licensed under the MIT License. See the `LICENSE` file for details. diff --git a/docs/BLOG.md b/docs/BLOG.md new file mode 100644 index 0000000..b9f92f2 --- /dev/null +++ b/docs/BLOG.md @@ -0,0 +1,190 @@ +# httpclient: A Modern, Lightweight HTTP Client for Go + +## Introduction + +`httpclient` is a lightweight and efficient HTTP client library written in Go. It simplifies making HTTP requests by providing an easy-to-use interface for GET, POST, PUT, DELETE, and other HTTP methods. This library is designed to handle common use cases like setting headers, query parameters, and handling JSON payloads, while also supporting advanced features like retries, exponential backoff, custom logging, and more. + +--- + +## Architecture + +The architecture of `httpclient` is modular and extensible, built around a core `HTTPClient` struct that wraps Go's standard `http.Client` and adds additional features via functional options. Here is a high-level architecture diagram: + +```mermaid +flowchart TD + A["User Code"] -->|"calls"| B["httpclient.NewHTTPClient()"] + B -->|"returns"| C["HTTPClient struct"] + C -->|"calls"| D["http.Client"] + C -->|"uses"| E["Options (WithRetry, WithTimeout, etc.)"] + C -->|"calls"| F["Do() method"] + F -->|"calls"| G["http.Client.Do()"] + F -->|"handles"| H["Retry, Logging, Backoff"] + C -->|"provides"| I["Helpers: Get, Post, Put, Delete, PostJSON"] + I -->|"wraps"| F + C -->|"uses"| J["headerTransport (default headers)"] + F -->|"returns"| K["http.Response"] + K -->|"used by"| L["ReadResponseBody, ReadJSONResponseBody"] +``` + +--- + +## Detailed Explanation + +### Core Components + +- **HTTPClient struct**: The main client, wrapping Go's `http.Client` and adding retry, logging, backoff, and default headers. +- **Options**: Functional options like `WithRetry`, `WithTimeout`, `WithLogger`, `WithTransport`, `WithTLSConfig`, and `WithDefaultHeaders` allow you to customize the client. +- **Helpers**: Methods like `Get`, `Post`, `Put`, `Delete`, and `PostJSON` provide a simple interface for common HTTP operations. +- **Retry & Backoff**: Built-in support for retrying failed requests, with optional exponential backoff. +- **Custom Logging**: Plug in your own logger for request/response logging. +- **Default Headers**: Set headers to be included in every request. +- **Response Helpers**: Utility functions to read and unmarshal response bodies. + +### Why Use httpclient? +- **Simplicity**: Clean, easy-to-use API for common HTTP tasks. +- **Extensibility**: Add custom behavior via options. +- **Reliability**: Automatic retries and backoff for transient errors. +- **Testability**: Well-tested with unit tests and easy to mock. + +--- + +## Installation + +```bash +go get github.com/chinnareddy578/httpclient +``` + +## Importing + +```go +import "github.com/chinnareddy578/httpclient" +``` + +--- + +## Usage + +### Creating a Client + +```go +client := httpclient.NewHTTPClient( + httpclient.WithTimeout(5 * time.Second), + httpclient.WithRetry(3, 1 * time.Second), + httpclient.WithLogger(log.Default()), + httpclient.WithDefaultHeaders(map[string]string{"X-App": "myapp"}), +) +``` + +### GET Request + +```go +headers := map[string]string{"Authorization": "Bearer token"} +resp, err := client.Get("https://api.example.com/data", headers) +if err != nil { + log.Fatal(err) +} +body, _ := httpclient.ReadResponseBody(resp) +fmt.Println(body) +``` + +### POST Request (Raw Body) + +```go +body := bytes.NewBufferString("raw payload") +headers := map[string]string{"Content-Type": "text/plain"} +resp, err := client.Post("https://api.example.com/upload", body, headers) +if err != nil { + log.Fatal(err) +} +bodyStr, _ := httpclient.ReadResponseBody(resp) +fmt.Println(bodyStr) +``` + +### POST Request (JSON) + +```go +payload := map[string]interface{}{"name": "John", "age": 30} +headers := map[string]string{"Authorization": "Bearer token"} +resp, err := client.PostJSON("https://api.example.com/users", payload, headers) +if err != nil { + log.Fatal(err) +} +var result map[string]interface{} +_ = httpclient.ReadJSONResponseBody(resp, &result) +fmt.Println(result) +``` + +### PUT Request + +```go +update := map[string]interface{}{"age": 31} +updateBody, _ := json.Marshal(update) +headers := map[string]string{"Content-Type": "application/json"} +resp, err := client.Put("https://api.example.com/users/1", bytes.NewReader(updateBody), headers) +if err != nil { + log.Fatal(err) +} +bodyStr, _ := httpclient.ReadResponseBody(resp) +fmt.Println(bodyStr) +``` + +### DELETE Request + +```go +headers := map[string]string{"Authorization": "Bearer token"} +resp, err := client.Delete("https://api.example.com/users/1", headers) +if err != nil { + log.Fatal(err) +} +bodyStr, _ := httpclient.ReadResponseBody(resp) +fmt.Println(bodyStr) +``` + +--- + +## Advanced Features + +- **Retries & Exponential Backoff**: Use `WithRetry` and `WithExponentialBackoff` to handle transient errors gracefully. +- **Custom Logger**: Pass your own logger for detailed request/response logs. +- **Custom Transport & TLS**: Use `WithTransport` and `WithTLSConfig` for advanced networking needs. +- **Default Headers**: Set headers to be included in every request. + +--- + +## Submitting Suggestions and Issues + +We welcome suggestions and issues! Please use the [GitHub Issues page](https://github.com/chinnareddy578/httpclient/issues) to submit bugs, feature requests, or general feedback. + +--- + +## Contributing + +1. Fork the repository on GitHub. +2. Create a new branch for your feature or bug fix: + ```bash + git checkout -b feature-name + ``` +3. Make your changes and commit them with clear and concise messages. +4. Push your changes to your forked repository: + ```bash + git push origin feature-name + ``` +5. Open a pull request on the main repository. + +### Guidelines +- Ensure your code follows Go best practices. +- Write tests for any new features or bug fixes. +- Update the documentation if necessary. + +--- + +## References + +- [GitHub Repository](https://github.com/chinnareddy578/httpclient) +- [GoDoc Documentation](https://pkg.go.dev/github.com/chinnareddy578/httpclient) + +--- + +## License + +This project is licensed under the MIT License. See the `LICENSE` file for details. \ No newline at end of file