Writing a good Go SDK
The typical way of building a client in Go is to pass a *http.Client
in order to let the caller set timeouts and so on. See godo (Digital Ocean API client) for a good way of doing it.
Another good practice is to avoid hidden network calls. For example, most Go users do not expect a NewClient
function to do a network call. Prefer using a dedicated function such as client.Auth
.
In the [docker][] client
NewClient
should probably only take an*http.Client
and work from there.
I think NewClient
should only take a token and we also offer the user a way to give the token:
func NewClient(c *http.Client, token string) Client {
}
func GetToken(
Content:
- Unleash
- Stripe
- Redis
- Saltstack client (r3labs/go-salt)
- Docker Engine API client
- Heroku
- Slack
- Scaleway
Unleash
import "github.com/Unleash/unleash-client-go"
func init() {
unleash.Initialize(
unleash.WithListener(&unleash.DebugListener{}),
unleash.WithAppName("my-application"),
unleash.WithUrl("http://unleash.herokuapp.com/api/"),
)
}
func main() {
// Example of API call.
unleash.IsEnabled("app.ToggleX")
}
Stripe
import (
"net/http"
stripe "github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/client"
)
func main() {
httpClient := &http.Client{Timeout: 10 * time.Second}
backends := stripe.NewBackends(httpClient)
client := client.NewClient("token", backends)
}
Redis
Doesn’t use HTTP anyway. But you can pass a Dialer function that creates a net.Conn
. Since Redis is using TCP directly, the Redis client has to offer everything itself (e.g. DialTimeout).
import "github.com/go-redis/redis"
func main() {
opts := &redis.Options{Addr: "localhost:6379",Password: ""}
client := redis.NewClient(opts)
}
Saltstack client (r3labs/go-salt)
The NewClient does a network call. That’s pretty uncommon to do that in Go.
There is no way to pass a *http.Client
in NewClient
. But you can by going through some hoops.
Also, the authentication should create a new *http.Client
instead of mutating the conn.AuthToken
value.
import (
"net/http"
"github.com/r3labs/go-salt"
)
func main() {
// Using NewClient
opts := salt.Config{Host: "", Username: "", Password: ""}
client, err := salt.NewClient(opts)
// With a timeout using *http.Client
httpClient := &http.Client{Timeout: 10 * time.Second}
conn := salt.Connector{Client: httpClient, AuthToken: "token"}
conn.Authenticate()
client := &salt.Client{Connector: conn}
job, err := client.Job("foo")
}
Docker Engine API client
The dockerengine.NewClient does return an error, which is kind of unusual for NewClient in Go. The only thing they do is to check that the transport field (http.Transport
) is in fact a http.RoundTripper
.
import (
"net/http"
"github.com/moby/moby/client"
)
func main() {
httpClient := &http.Client{Timeout: 10 * time.Second}
client, err := client.NewClientWithOpts(client.WithHTTPClient(httpClient))
if err != nil {
panic(err)
}
Heroku
Very clean! Many globals, but it’s just for ease of use: everything can be used without using the global state (heroku.DefaultTransport
). https://github.com/heroku/heroku-go/blob/master/v5/transport.go
import (
heroku "github.com/heroku/heroku-go/v5"
)
func main() {
c := &http.Client{
Timeout: 10 * time.Second,
Transport: heroku.Transport{BearerToken: "token"},
}
h := heroku.NewService(c)
addons, err := h.AddOnList(context.TODO(), &heroku.ListRange{Field: "name"})
}
Slack
Very good!!! https://github.com/nlopes/slack/blob/e5749f13b5af3c139165ab4180a95bb06a60128b/slack.go#L68
import (
"github.com/nlopes/slack"
)
func main() {
httpClient := &http.Client{Timeout: 10 * time.Second}
api := slack.New("token", slack.OptionHTTPClient(httpClient))
groups, err := api.GetGroups(false)
}
Scaleway
https://github.com/scaleway/scaleway-sdk-go
And they use an HTTP recorder “à la Jest Snapshot”!