Go Design
OSS Comparison
For a comparison between the Speakeasy Go SDK and some popular open-source generators, see this page.
Speakeasy Go SDKs are designed to be easy to use and easy to debug. Various decisions were made that guide the design of the SDK; these include:
- Minimal dependencies and relying on the Go standard library as much as possible.
- Struct tags and reflection-based (de)serializers to define how the types we generate are correctly serialized based on the OpenAPI document.
- Pointers for optional objects including fields, parameters, and response and request bodies to ensure the user can differentiate between a field not being set and a field being set to a zero value.
- A
utils
package that improves readability by bundling the methods for configuring the SDK and serializing/deserializing the types we generate into a shared package, avoiding the need to duplicate in each method.
Examples
Guardrails
Easy-to-use Go SDKs that minimize the time to 200 should reduce the opportunity for users to send requests that don't match the API spec.
To that end, Go SDKs generated by Speakeasy convey maximal information to the end user and, where possible, actively restrict inputs.
Here's the Pet
model generated by Swagger CodeGen (opens in a new tab) using the Swagger Petstore OpenAPI schema (opens in a new tab):
package swagger
type Pet struct {
Id int64 `json:"id,omitempty"`
Name string `json:"name"`
Category *Category `json:"category,omitempty"`
PhotoUrls []string `json:"photoUrls"`
Tags []Tag `json:"tags,omitempty"`
// pet status in the store
Status string `json:"status,omitempty"`
}
- Required parameters are only differentiated from optional parameters by the presence of the
omitempty
annotation. - Despite
Status
having only three valid values (available
,pending
, andsold
) in the OpenAPI spec, it is represented as a string.
Compare this to the Pet
model generated by Speakeasy:
package shared
type PetStatusEnum string
const (
PetStatusEnumAvailable PetStatusEnum = "available"
PetStatusEnumPending PetStatusEnum = "pending"
PetStatusEnumSold PetStatusEnum = "sold"
)
type Pet struct {
Category *Category `json:"category,omitempty"`
ID *int64 `json:"id,omitempty"`
Name string `json:"name"`
PhotoUrls []string `json:"photoUrls"`
Status *PetStatusEnum `json:"status,omitempty"`
Tags []Tag `json:"tags,omitempty"`
}
- Optional parameters are indicated by pointers, permitting them to evaluate as
nil
. This is especially useful when the model is received as the response from an API call, as the user can distinguish between unset (nil
) and the default. - The
Status
parameter is an enum, guiding the user to provide one of the three permissible values.
Authorization
Communicating authorization methods can be tricky, especially when there are multiple methods of authenticating and different styles of authorization for various endpoints, as in the Swagger Petstore OpenAPI schema (opens in a new tab).
Here's an example of how Speakeasy handles a request with a specific authentication scheme:
package operations
type FindPetsByStatusStatusEnum string
// ...
type FindPetsByStatusQueryParams struct {
Status *FindPetsByStatusStatusEnum `queryParam:"style=form,explode=true,name=status"`
}
type FindPetsByStatusSecurity struct {
PetstoreAuth shared.SchemePetstoreAuth `security:"scheme,type=oauth2"`
}
type FindPetsByStatusRequest struct {
QueryParams FindPetsByStatusQueryParams
Security FindPetsByStatusSecurity
}
type FindPetsByStatusResponse struct {
Body []byte
ContentType string
Pets []shared.Pet
StatusCode int64
}
Note that the exact type of authentication required (shared.SchemePetstoreAuth
) is specified in the request.
Compare that to the code generated by Swagger CodeGen (opens in a new tab):
type PetApiFindPetsByStatusOpts struct {
Status optional.String
}
Security must be set through the Context
passed into the FindPetsByStatus
method, and nowhere is it communicated what style of authentication is required for each endpoint.
Getter Methods
In addition to the fields, Speakeasy generates getter methods for each field, allowing users to access the fields by chaining getters without having to worry about nil
.
Here are a couple of the getter methods generated for the Pet
model:
func (o *Pet) GetCategory() *Category {
if o == nil {
return nil
}
return o.Category
}
func (o *Pet) GetName() string {
if o == nil {
return ""
}
return o.Name
}
The nil
check allows these methods to be safely chained to simplify code that doesn't need all the extra checking. For example,
client := sdk.NewClient()
pet, err := client.Pet.GetPetByID(17)
if err != nil {
// TODO handle error
}
petCategory := pet.GetCategory().GetName()
The above code will not panic, regardless of what the API returns. This remains true despite skipping error handling with the TODO
comment, and would also be the case if there were an error that resulted in pet == nil
. If the optional Category
is not set, the getter generated for that model will also do the same nil
check and avoid triggering a panic.
If you have any feedback or want to suggest improvements or ask for a new feature, please get in touch in the #client-sdks
channel in our public Slack (opens in a new tab).