All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
-
Feature: Extra properties decoded from response objects are retained and accessible via the
GetExtraPropertiesmethod like so:user, err := client.Users.Get(...) if err != nil { return nil, err } for key, value := range user.GetExtraProperties() { fmt.Printf("Got extra property; key: %s, value: %v\n", key, value) }
- Internal: The generator now uses the latest FDR SDK.
- Fix: In-lined request body properties no longer include a non-empty
urlstruct tag. This previously caused request body properties to be encoded in the URL alongside the rest of the query parameters.
-
Fix: The Go generator now escapes path parameters that would previously create invalid URLs (e.g. "\example").
-
Improvement: Refactor endpoint URL mapping with
core.EncodeURL. All generated endpoints with path parameters now see use cases like the following:endpointURL := core.EncodeURL(baseURL+"/organizations/%v", orgID)
-
Feature: Add support for cursor and offset pagination.
For example, consider the following endpoint
/usersendpoint:types: User: properties: name: string ListUserResponse: properties: next: optional<string> data: list<User> service: auth: false base-path: /users endpoints: list: path: "" method: GET pagination: cursor: $request.starting_after next_cursor: $response.next results: $response.data request: name: ListUsersRequest query-parameters: starting_after: optional<string> response: ListUsersResponse
The generated
client.Users.Listmethod now returns a genericcore.Page[T]that can be used to fetch the next page like so:page, err := client.Users.List( ctx, &acme.ListUsersRequest{ StartingAfter: acme.String("user_xyz"), }, ) if err != nil { return nil, err } for page != nil { for _, user := range page.Results { fmt.Printf("Got user: %v\n", user.Name) } page, err = page.GetNextPage() if errors.Is(err, core.ErrNoPages) { break } if err != nil { // Handle the error! } }
If you don't need to explicitly request every individual page, you can convert the
core.Pageinto acore.PageIteratorand simply iterate over each element like so:page, err := client.Users.List( ctx, &acme.ListUsersRequest{ StartingAfter: acme.String("user_xyz"), }, ) if err != nil { return nil, err } iter := page.Iterator() for iter.Next() { user := iter.Current() fmt.Printf("Got user: %v\n", user.Name) } if err := iter.Err(); err != nil { // Handle the error! }
The iterator will automatically fetch the next page as needed and continue to iterate until all the pages are read.
-
Improvement: Enhance extra property serialization performance.
-
Improvement: Generate additional extra property tests into the SDK.
-
Fix: Resolve an non-deterministic key ordering issue for snippets of type
unknown. -
Fix: Resolve an issue with discriminated union serialization. This only occurs when the union varaint requires its own custom JSON serialization strategy and the union variant contains the same properties as another object.
For example, consider the following union definition:
Circle: properties: created_at: datetime Square: properties: created_at: datetime Shape: union: circle: Circle square: Square
The generated
json.Marshalermethod now looks like the following, where the discriminant is added directly to the serialized JSON object:func (s Shape) MarshalJSON() ([]byte, error) { if s.Circle != nil { return core.MarshalJSONWithExtraProperty(s.Circle, "type", "circle") } if s.Square != nil { return core.MarshalJSONWithExtraProperty(s.Square, "type", "square") } return nil, fmt.Errorf("type %T does not define a non-empty union type", s) }
-
Fix: The
omitemptystruct tag is now only used for nil-able types. It was previously used for non-optional enums, which was never intended. For example, the followingRequestTypeenum will no longer include anomitemptytag:type Request struct { Type RequestType `json:"type" url:"type"` }
-
Fix: Update the query encoder to prevent unintentional errors whenever the
omitemptyis used for a non-optional field.
-
Feature: The Go generator now supports extra properties.
For example, consider the following type definition:
types: User: extra-properties: true properties: name: string
The generated
Usertype will now have anExtraPropertiesfield like so:type User struct { Name string `json:"name" url:"name"` ExtraProperties map[string]interface{} `json:"-" url:"-"` }
If any extra properties are set, they will be sent alongside the rest of the defined properties, e.g. the
agekey in{"name": "alice", "age": 42}.
-
Feature: The Go generator now supports environment variable scanning.
For example, consider the following
api.ymldefinition:name: api auth: Bearer auth-schemes: Bearer: scheme: bearer token: name: apiKey env: ACME_API_KEY
The client reads this environment variable and sets the value in the
Authorizationheader if theAPIKeyoption is not explicitly specified like so:func NewClient(opts ...option.RequestOption) *Client { options := core.NewRequestOptions(opts...) if options.APIKey == "" { options.APIKey = os.Getenv("ACME_API_KEY") } return &Client{ baseURL: options.BaseURL, caller: core.NewCaller( &core.CallerParams{ Client: options.HTTPClient, MaxAttempts: options.MaxAttempts, }, ), // The header associated with the client will contain // the ACME_API_KEY value. // // It can still be overridden by endpoint-level request // options. header: options.ToHeader(), } }
-
Fix: Path parameters are now applied in the correct order. This is relevant for endpoints that specify more than one path parameter (e.g.
/organizations/{orgId}/users/{userId}). Function signatures remain unchanged, such that they preserve the path parameter order specified by the API definition like so:func (c *Client) Get( ctx context.Context, userID string, orgID string, opts ...option.RequestOption, ) (*acme.User, error) { ... endpointURL := fmt.Sprintf(baseURL+"/"+"organizations/%v/users/%v", orgID, userID) ... }
- Fix: Custom authorization header schemes had their values overridden by request options, which required using the generated request option at every call-site.
- Fix: Go snippets correctly handle unknown examples.
response, err := client.CreateUser(
ctx,
&acme.CreateUserRequest{
Name: "alice",
Metadata: map[string]interface{}{
"address": "123 Market Street",
"age": 28,
},
},
)- Feature: Add support for simpler unions, which is configurable with
union: v1(if omitted, the defaultv0version will be used). Withv0, a separate constructor for each variant of the union was generated, but using these constructors is cumbersome for large production APIs due to the sheer length of the function name. This improves the experience by simply setting the discriminant at runtime, which prevents the need for constructors entirely.
- name: fernapi/fern-go-sdk
version: 0.18.0
config:
union: v1// Before
union := acme.NewStatusFromCloudServerAlertStatus(
&acme.CloudServerAlertStatus{
Value: "WARNING",
},
)// After
union := &acme.Status{
CloudServerAlertStatus: &acme.CloudServerAlertStatus{
Value: "WARNING",
},
)- Feature: Add support for multiple files in upload endpoints. Endpoints that specify
multiple file parameters will include a
[]io.Readerparameter, where each value is individually named. If theio.Readerdoes not contain a name, a name is generated.
func (c *Client) Upload(
ctx context.Context,
fileList []io.Reader,
opts ...option.RequestOption,
) error {
...
}- No changes since previous release candidate.
- Fix: Snippets for aliases to optional primitive values. With this, the generated snippet
will include the top-level pointer helpers (e.g.
acme.String(...)).
- Fix: Package documentation is now generated into the correct package's
doc.go. - Feature: Add support for generated endpoint snippets.
- The snippets will be available in the API reference documentation, as well as the snippets API.
- The snippets are not embedded in the SDK itself (yet).
import (
context "context"
time "time"
acme "github.com/acme/acme-go"
acmeclient "github.com/acme/acme-go/client"
option "github.com/acme/acme-go/option"
)
client := acmeclient.NewClient(
option.WithToken(
"<YOUR_AUTH_TOKEN>",
),
)
response, err := client.User.Create(
context.TODO(),
&acme.CreateUserRequest{
Username: "john",
Language: acme.LanguageEnglish,
Age: acme.Int(32),
Birthday: acme.MustParseDate(
"1980-06-01"
),
},
)-
Feature: The generator now supports whitelabelling. When this is turned on, there will be no mention of Fern in the generated code.
Note: You must be on the enterprise tier to enable this mode.
- Feature: Enforce RFC3339 for date[time] serialization in request bodies.
- Fix: Query parameter supoort for optional
time.Timetypes.
- Feature: Add support for
deepObjectquery parameters. - Chore: Refactor query parameter serialization with
urlstruct tags.
- Feature: Add
packageNamegenerator configuration. - Feature: Add support for
bytesrequest bodies wrapped in an in-lined request.
- Fix:
text/plainresponse handling.
- Feature: Add support for
bytesrequest bodies withContent-Typeset toapplication/octet-stream.
- Feature: Add automatic retry with exponential backoff.
- Feature: Refactor
ClientOptionasRequestOption. - Feature: Add
includeLegacyClientOptionsgenerator configuration. - Feature: Support idempotency headers as a special
RequestOptiononly available on idempotent endpoints. - Fix: Placement of path parameter documentation.
- Fix: Naming collision issue for undiscriminated unions that define more than one literal.
- Fix: File upload requests that specify query parameters.
- Fix: Optional query parameter dereferencing issue.
- Fix: Append version suffix for modules tagged with major versions greater than
1.X.X.
- Fix: Support boolean literals.
- Fix: Union subt-ypes with no properties are now go 1.13 compatible.
- Feature: Add support for streaming endpoints.
- Feature: Add support for non-primitive file upload properties.
- Chore: Refactor
core.DoRequestwithcore.Callerabstraction. - Chore: Update pinned dependencies in generated
go.mod.