Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sonirico/withttp
go build http 🌐 requests with fluency and wit ✨
https://github.com/sonirico/withttp
builder fluent-api generics golang golang-library hacktoberfest http http-client http-requests https
Last synced: 15 days ago
JSON representation
go build http 🌐 requests with fluency and wit ✨
- Host: GitHub
- URL: https://github.com/sonirico/withttp
- Owner: sonirico
- License: mit
- Created: 2022-08-15T21:40:35.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2023-02-15T19:33:41.000Z (almost 2 years ago)
- Last Synced: 2024-05-01T15:29:04.315Z (8 months ago)
- Topics: builder, fluent-api, generics, golang, golang-library, hacktoberfest, http, http-client, http-requests, https
- Language: Go
- Homepage:
- Size: 106 KB
- Stars: 19
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
![build](https://github.com/sonirico/withttp/actions/workflows/go.yml/badge.svg)
# withttp
Build http requests and parse their responses with fluent syntax and wit. This package aims
to quickly configure http roundtrips by covering common scenarios, while leaving all details
of http requests and responses open for developers to allow maximum flexibility.Supported underlying http implementations are:
- [net/http](https://pkg.go.dev/net/http)
- [fasthttp](https://pkg.go.dev/github.com/valyala/fasthttp)
- Wrap your client with [Client interface](https://github.com/sonirico/withttp/blob/main/endpoint.go#L43) ...
- ... or open an issue to include your preferred one!#### Query Restful endpoints
```go
type GithubRepoInfo struct {
ID int `json:"id"`
URL string `json:"html_url"`
}func GetRepoInfo(user, repo string) (GithubRepoInfo, error) {
call := withttp.NewCall[GithubRepoInfo](withttp.WithFasthttp()).
WithURL(fmt.Sprintf("https://api.github.com/repos/%s/%s", user, repo)).
WithMethod(http.MethodGet).
WithHeader("User-Agent", "withttp/0.5.1 See https://github.com/sonirico/withttp", false).
WithParseJSON().
WithExpectedStatusCodes(http.StatusOK)err := call.Call(context.Background())
return call.BodyParsed, err
}func main() {
info, _ := GetRepoInfo("sonirico", "withttp")
log.Println(info)
}
```#### Stream data to server (from a slice)
[See full example](https://github.com/sonirico/withttp/blob/main/examples/request_stream/main.go)
```go
type metric struct {
Time time.Time `json:"t"`
Temp float32 `json:"T"`
}func CreateStream() error {
points := []metric{
{
Time: time.Unix(time.Now().Unix()-1, 0),
Temp: 39,
},
{
Time: time.Now(),
Temp: 40,
},
}stream := withttp.Slice[metric](points)
testEndpoint := withttp.NewEndpoint("webhook-site-request-stream-example").
Request(
withttp.WithBaseURL("https://webhook.site/24e84e8f-75cf-4239-828e-8bed244c0afb"),
)call := withttp.NewCall[any](withttp.WithFasthttp()).
WithMethod(http.MethodPost).
WithContentType(withttp.ContentTypeJSONEachRow).
WithRequestSniffed(func(data []byte, err error) {
fmt.Printf("recv: '%s', err: %v", string(data), err)
}).
WithRequestStreamBody(
withttp.WithRequestStreamBody[any, metric](stream),
).
WithExpectedStatusCodes(http.StatusOK)return call.CallEndpoint(context.Background(), testEndpoint)
}
```#### Stream data to server (from a channel)
[See full example](https://github.com/sonirico/withttp/blob/main/examples/request_stream/main.go)
```go
func CreateStreamChannel() error {
points := make(chan metric, 2)go func() {
points <- metric{
Time: time.Unix(time.Now().Unix()-1, 0),
Temp: 39,
}points <- metric{
Time: time.Now(),
Temp: 40,
}close(points)
}()stream := withttp.Channel[metric](points)
testEndpoint := withttp.NewEndpoint("webhook-site-request-stream-example").
Request(
withttp.WithBaseURL("https://webhook.site/24e84e8f-75cf-4239-828e-8bed244c0afb"),
)call := withttp.NewCall[any](withttp.WithFasthttp()).
WithMethod(http.MethodPost).
WithContentType(withttp.ContentTypeJSONEachRow).
WithRequestSniffed(func(data []byte, err error) {
fmt.Printf("recv: '%s', err: %v", string(data), err)
}).
WithRequestStreamBody(
withttp.WithRequestStreamBody[any, metric](stream),
).
WithExpectedStatusCodes(http.StatusOK)return call.CallEndpoint(context.Background(), testEndpoint)
}
```#### Stream data to server (from a reader)
[See full example](https://github.com/sonirico/withttp/blob/main/examples/request_stream/main.go)
```go
func CreateStreamReader() error {
buf := bytes.NewBuffer(nil)go func() {
buf.WriteString("{\"t\":\"2022-09-01T00:58:15+02:00\"")
buf.WriteString(",\"T\":39}\n{\"t\":\"2022-09-01T00:59:15+02:00\",\"T\":40}\n")
}()streamFactory := withttp.NewProxyStreamFactory(1 << 10)
stream := withttp.NewStreamFromReader(buf, streamFactory)
testEndpoint := withttp.NewEndpoint("webhook-site-request-stream-example").
Request(
withttp.WithBaseURL("https://webhook.site/24e84e8f-75cf-4239-828e-8bed244c0afb"),
)call := withttp.NewCall[any](withttp.WithNetHttp()).
WithMethod(http.MethodPost).
WithRequestSniffed(func(data []byte, err error) {
fmt.Printf("recv: '%s', err: %v", string(data), err)
}).
WithContentType(withttp.ContentTypeJSONEachRow).
WithRequestStreamBody(
withttp.WithRequestStreamBody[any, []byte](stream),
).
WithExpectedStatusCodes(http.StatusOK)return call.CallEndpoint(context.Background(), testEndpoint)
}
```#### Several endpoints
In case of a wide range catalog of endpoints, predefined parameters and behaviours can be
defined by employing an endpoint definition.```go
var (
githubApi = withttp.NewEndpoint("GithubAPI").
Request(withttp.WithBaseURL("https://api.github.com/"))
)type GithubRepoInfo struct {
ID int `json:"id"`
URL string `json:"html_url"`
}func GetRepoInfo(user, repo string) (GithubRepoInfo, error) {
call := withttp.NewCall[GithubRepoInfo](withttp.WithFasthttp()).
WithURI(fmt.Sprintf("repos/%s/%s", user, repo)).
WithMethod(http.MethodGet).
WithHeader("User-Agent", "withttp/0.5.1 See https://github.com/sonirico/withttp", false).
WithHeaderFunc(func() (key, value string, override bool) {
key = "X-Date"
value = time.Now().String()
override = true
return
}).
WithParseJSON().
WithExpectedStatusCodes(http.StatusOK)err := call.CallEndpoint(context.Background(), githubApi)
return call.BodyParsed, err
}type GithubCreateIssueResponse struct {
ID int `json:"id"`
URL string `json:"url"`
}func CreateRepoIssue(user, repo, title, body, assignee string) (GithubCreateIssueResponse, error) {
type payload struct {
Title string `json:"title"`
Body string `json:"body"`
Assignee string `json:"assignee"`
}p := payload{
Title: title,
Body: body,
Assignee: assignee,
}call := withttp.NewCall[GithubCreateIssueResponse](
withttp.WithFasthttp(),
).
WithURI(fmt.Sprintf("repos/%s/%s/issues", user, repo)).
WithMethod(http.MethodPost).
WithContentType("application/vnd+github+json").
WithBody(p).
WithHeaderFunc(func() (key, value string, override bool) {
key = "Authorization"
value = fmt.Sprintf("Bearer %s", "S3cret")
override = true
return
}).
WithExpectedStatusCodes(http.StatusCreated)err := call.CallEndpoint(context.Background(), githubApi)
log.Println("req body", string(call.Req.Body()))
return call.BodyParsed, err
}func main() {
// Fetch repo info
info, _ := GetRepoInfo("sonirico", "withttp")
log.Println(info)// Create an issue
res, err := CreateRepoIssue("sonirico", "withttp", "test",
"This is a test", "sonirico")
log.Println(res, err)
}
```#### Test your calls again a mock endpoint
Quickly test your calls by creating a mock endpoint
```go
var (
exchangeListOrders = withttp.NewEndpoint("ListOrders").
Request(withttp.WithBaseURL("http://example.com")).
Response(
withttp.WithMockedRes(func(res withttp.Response) {
res.SetBody(io.NopCloser(bytes.NewReader(mockResponse)))
res.SetStatus(http.StatusOK)
}),
)
mockResponse = []byte(strings.TrimSpace(`
{"amount": 234, "pair": "BTC/USDT"}
{"amount": 123, "pair": "ETH/USDT"}`))
)func main() {
type Order struct {
Amount float64 `json:"amount"`
Pair string `json:"pair"`
}res := make(chan Order)
call := withttp.NewCall[Order](withttp.WithFasthttp()).
WithURL("https://github.com/").
WithMethod(http.MethodGet).
WithHeader("User-Agent", "withttp/0.5.1 See https://github.com/sonirico/withttp", false).
WithParseJSONEachRowChan(res).
WithExpectedStatusCodes(http.StatusOK)go func() {
for order := range res {
log.Println(order)
}
}()err := call.CallEndpoint(context.Background(), exchangeListOrders)
if err != nil {
panic(err)
}
}
```### TODO:
- Form-data content type codecs
- More quality-of-life methods for auth
- Able to parse more content types:
- tabular separated
- xml
- gRPC