Skip to content
/ gr Public

Short for "Go Request-Response". Shortcuts for making HTTP requests and reading HTTP responses in Go.

License

Notifications You must be signed in to change notification settings

mitranim/gr

Repository files navigation

Overview

"gr" stands for for Go Request or Go Request-Response. It also represents my reaction to many APIs in "net/http". Shortcuts for making HTTP requests and reading HTTP responses. Features:

  • Brevity!
  • No added wrappers or interfaces. Just aliases for http.Request and http.Response, freely-castable back and forth.
  • Fluent chainable builder API.
  • Most methods are nullary or unary. No syntactic overhead for features you don't use.
  • No overhead over "lower-level" uses of http.Request and http.Response. Many shortcuts are more performant than "standard" approaches.
  • Resulting requests can be passed to any code that takes http.Request.
  • Usable for http.Response responses obtained from any source.
  • Tiny and dependency-free.

API docs: https://pkg.go.dev/github.com/mitranim/gr.

Usage

Sending and receiving JSON:

package gr_test

import (
  "fmt"
  "net/url"

  "github.com/mitranim/gr"
)

func ExampleReq_jsonInputJsonOutput() {
  input := Input{`some input`}

  var output Output
  gr.To(testServer.URL).Path(`/json`).Json(input).Res().Ok().Json(&output)

  fmt.Printf("%#v\n", output)

  // Output:
  // gr_test.Output{ReqMethod:"GET", ReqUrl:"/json", ReqBody:"{\"inputVal\":\"some input\"}"}
}

type Input struct {
  InputVal string `json:"inputVal"`
}

type Output struct {
  ReqMethod string `json:"reqMethod"`
  ReqUrl    string `json:"reqUrl"`
  ReqBody   string `json:"reqBody"`
}

Sending URL-encoded form, reading plain text:

package gr_test

import (
  "fmt"
  "net/url"

  "github.com/mitranim/gr"
)

func ExampleReq_formBodyPlainResponse() {
  req := gr.To(testServer.URL).Post().FormVals(url.Values{`one`: {`two`}})
  res := req.Res().Ok()
  defer res.Done()

  fmt.Printf("\nresponse status: %v\n", res.StatusCode)
  fmt.Printf("\nresponse type: %v\n", res.Header.Get(gr.Type))
  fmt.Printf("\nresponse body:\n%v\n", res.ReadString())

  // Output:
  //
  // response status: 200
  //
  // response type: text/plain; charset=utf-8
  //
  // response body:
  //
  // request method: POST
  // request URL: /
  // request body: one=two
}

Why Pointers

Since gr uses a chainable builder-style API, we could have defined all "builder" methods on gr.Req and gr.Res (non-pointer types), rather than on *gr.Req and *gr.Res (pointer types). This would allow to store "partially built" requests, and "fork" them by simply reassigning variables. So why do we define this on pointer types?

  • In Go, request and response are inherently mutable because they contain reference types such as *url.URL, http.Header and io.ReadCloser. Copying structs such as http.Request or http.Response by reassigning variables makes a shallow copy where the inner references are still mutably shared. That would be hazardous. Explicit copying via .Clone is less error prone.

  • Emulating an "immutable" API by using copy-on-write for URL and headers is possible, but incurs a measurable performance penalty.

  • All APIs in "net/http" operate on requests and responses by pointer. By using the same pointers, we avoid the overhead of copying and reallocation.

  • Go request and response structs are rather large. The language seems to use naive call conventions that involve always copying value types, as opposed to passing them by reference when they would be constant. For large structs, always passing them by pointer rather than by value seems faster.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts

About

Short for "Go Request-Response". Shortcuts for making HTTP requests and reading HTTP responses in Go.

Topics

Resources

License

Stars

Watchers

Forks