Skip to main content

APIMatic Code Generator V3

· 26 min read

APIMatic V3 (codenamed Titan) is APIMatic's largest release to date, bringing some major improvements to the Code Generator, the API Portal and the portal publishing flow. This changelog covers the new features and improvements in the updated APIMatic Code Generator which now generates vastly improved SDKs.

Note that as part of this release, Ruby, Python, C# and Java code generators are available. A later release will add support for PHP and Node.js soon.

Since a lot of these changes are breaking changes, we suggest that you test your SDKs thoroughly after generating them and publishing them as a new major version.

Checkout all the new features and changes below:

Updated Language and Runtime Versions Requirements

A major part of this release included updating the target language and runtime versions in all the SDKs we generate.

We chose the new minimum language and runtime versions with a focus on balancing support for new features and improvements that the new versions bring with our aim for supporting the most commonly used versions in the ecosystem.

The new minimum language and runtime requirements are:

Language/PlatformSupported Versions
C# (a.k.a .NET).NET Standard 2.0.
JavaVersion 8+
PythonVersion 3.7 to 3.9
RubyVersion >= 2.5, < 3.0
PHPVersion >= 7.2

Not only do the SDKs target an updated minimum version, but they have also been refactored to use new language features where possible.

For example, as part of moving to Java 8, we updated the Java SDKs to use the new Date and Time utilities under java.time in the Java standard library. Previously, we were using the Jodatime library which provided a quality replacement for the buggy Date and Time classes in the standard library prior to Java 8. Another major change in Java SDKs is the use of CompleteableFutures in endpoints methods.

For more details, checkout the documentation on SDK Language Version Support.

Update to Dependencies

As part of migrating the SDKs to newer language and runtime versions, we also updated the dependencies used in the SDKs to use newer versions where possible. These dependencies include HTTP client libraries and other utility libraries needed by the SDK to send and receive JSON in the API calls. Our goal was again to enable compatibility with the maximum number of user codebase where these SDKs would be installed.

Note that we also monitor the dependencies closely for security vulnerability and where possible, provide a resolution as early as possible.

For more details on dependencies, check out the documentation on Supported SDK Dependencies.

Applying Coding Standards

One of the goals of the APIMatic CodeGen is to generate clean, readable code that would otherwise have been written by a human being. In order to keep the code inside our SDKs to a reasonable standard, our SDKs adhere to popular coding conventions which we test for using code style analyzers and linting tools.

As part of this release, we have updated the Code Generator so that the generated Java and C# SDKs now adhere to popular coding standards:

Our Ruby and Python SDKs were also kept up-to-date with the coding standards we follow for them.

For more information, check out our documentation about SDK Coding Standards.

New Immutable Clients in SDKs

One of the biggest design-related changes to the SDKs in this release is the refactoring of our SDKs into Immutable Clients.

An Immutable Client does not allow the user of the client to mutate the client (i.e change its state) once it has been created. At the beginning, this might look like a limitation to design our clients like this; however, to the contrary, Immutable Clients make it easier to write network-related code as code that deals with network requests (like the code written by the SDK users) often uses multi-threading or concurrency for performance or efficiency reasons. By making the clients immutable, it becomes easier to reason about multi-threaded or concurrent code because the immutable clients are inherently thread-safe and do not require explicit synchronization or defensive copying when working with them.

To make clients immutable, we make sure that all the necessary configuration is taken from the user at the time of the instantiation of the client. To make this easy for the user, we provide builders for the client in Java, C# and PHP; for Python and Ruby, the configuration is passed in as keyword arguments in the client's constructor. There are no setters on the client that allow the configuration to be modified once the client has been created. Instead, the clients can be cloned while changing some of the configuration. Cloning clients like this helps clone the internal state of the client while overriding only some configuration.

// Create new client instance
MyClient client = new MyClient.Builder()
.environment(Environment.PRODUCTION)
.httpClientConfig(config -> config.timeout(30))
.build();

// Clone client while modifying configuration
MyClient modifiedClient = client.newBuilder()
.environment(Environment.SANDBOX)
.build();

The new immutable design of the client also helps solve a long-standing issue in the SDKs where there was wide-spread usage of singleton in many places which prevented the users of the SDKs from creating multiple client instances with different configurations.

Timeout and Automatic Retries on API Call Errors

To create fault-tolerant network applications, it is important that an application does not cede control of the execution to another service or application. This principle is critical when writing applications that call APIs because API calls can timeout, return a temporary error, return a permanent error or become unreachable.

SDKs generated by APIMatic help you write fault-tolerant applications by helping deal with these kinds of errors. All SDKs come with support for timing out API calls that are taking too long and for retrying API calls that have a temporary error.

  • Timeouts ensure that the application is not stuck on an API call that is taking too long, perhaps because of an issue at the API server end or due to a network error.
  • Retries means retrying failing API calls in case of transient errors. Note that only API calls that are safe to retry (usually idempotent API calls) will be retried.

APIMatic uses an exponential back-off algorithm to calculate time between retries. The idea behind using exponential back-off with retry is that instead of retrying after waiting for a fixed amount of time, we increase the waiting time between retries after each retry failure. This gives the API server some breathing time so that if the fault is due to service overload, it could get resolved faster.

By default, all SDKs come with timeouts and retries enabled. You can configure the timeout and retries behavior when instantiating the client like this:

MyClient client = new MyClient.Builder()
.httpClientConfig(
config -> config.timeout(10)
.numberOfRetries(2)
.backOffFactor(2)
)
.build();

Access to HTTP Response Data

The APIMatic Code Generator can now be configured to create SDKs that also return the HTTP response information like response headers, status code and body on API calls. This feature is supported in all languages.

This feature is optional and can be turned on by setting the "Return Complete HTTP Response" CodeGen setting to true. This will change the endpoint methods so that they return the HTTP response data along with the deserialized or parsed form of the endpoint result.

This code sample shows how the HTTP response data can be accessed after an endpoint call:

 api.getDataAsync.thenAccept(response -> {
// response is an instance of ApiResponse<ENDPOINT_RESPONSE_TYPE>.

// You can access the response data and the deserialized body like this:
int responseStatusCode = response.getStatusCode();
Headers responseHeaders = response.getHeaders();
GetDataResponse result = response.getResult();
}).exceptionally(exception -> {
// Do something in case of an error...
return null;
});

Support for XML

We have added support for sending and receiving XML in the API calls. This is now supported in all SDKs. Previously, we only supported sending and receiving JSON in the request and response bodies in our SDK.

If you have an API definition that uses XML in the request or response bodies, you can now import it into APIMatic and generate SDKs that make the right API call. Since the SDKs wrap the API calls and hide the low-level HTTP details, the SDK user does not have to worry about what the XML request or response body looks like and what data needs to be sent as an attribute or as an element. The SDK is responsible for handling the correct serialization and deserialization of the data into and back from XML.

To start using this feature, you must document the XML request and responses for your API using the OpenAPI format or in one of the supported API specification formats. For more information, checkout this changelog about the Support for XML in API Transformer.

note

The API Portal has not yet been updated to work with API definitions that use XML. Therefore, the documentation on the API Portal might not be complete and some features like the "Try It Out" might now work. We are working on this. 😊

Also, we did not add support for XML namespaces and prefixes in the Code Generator because the OpenAPI specification is not clear on the semantics around them. Please reach out to our support in case this is blocking you.

Deprecating Endpoints

You can now mark endpoints as deprecated. An endpoint that is marked as deprecated will result in a compiler warning or a notice logged to the console when the endpoint method is called by the SDK user.

You can even provide a custom message and a deprecation version in your API definition which will be used by the SDK as part of the deprecation notice.

Check out how we set the endpoint as deprecated in an OpenAPI definition and how that translates into the SDK:

Check out this documentation on setting a endpoint as deprecated in OpenAPI.

Here's a quick glimpse at how you would mark an operation as deprecated in the OpenAPI definition:

"get": {
...
"operationId": "getData",
"summary": "Deprecated operation",
"deprecated": true,
"x-deprecation-details": {
"message": "Use the NewData endpoint instead.",
"deprecatedInVersion": "2.0"
}
}

Nullable Properties in Models

The Code Generator now supports generating Java, C# and PHP SDKs that contain nullable properties in models.

Previously, the Code Generator only supported optional properties which meant that properties that were not assigned a value by the user in the SDK were skipped and not sent in the HTTP request.

With full support for nullable properties, a null value will be sent in case the user does not assign a value to a property after initializing an instance of the model. In case a model is both nullable and optional, the user can either set null as the value or skip the property by "unsetting" it. Check out this example which demonstrates the usage of a property that is both nullable and optional:

SampleModel modelInstance = new SampleModel(OperationType.SUM, 2, 3);
model.setAnotherField("this is property is both optional AND nullable");

// EITHER: Unset property will not be sent in the JSON request
modelInstance.unsetAnotherField();

// OR: Null property will be sent as a "null" in the JSON request
modelInstance.setAnotherField(null)

Required Model Constructor Arguments

Required properties can now be set as part of the model instantiation in C#, Java and PHP SDKs.

A required property is one that is neither optional nor nullable. Since this property is required to be set for the API request to be correct, we have added them as required parameters to the model constructors.

Check out what the model instantiation looks like when there are required properties in a model:

// Required properties must be passed as required arguments.
// Optional and nullable properties can be set later.
SampleModel modelInstance = new SampleModel.Builder(
"required property value",
"another required property value"
)
.optionalProperty("optional property value")
.build();

While Ruby and Python accept arguments in the model constructors, these arguments are optional.

Cancellable API Calls in C#

All async endpoint methods in the generated C# SDKs now accept an optional CancellationToken as the last argument. This allows the SDK user to cancel the endpoint call if needed.

Let's say that you wanted to cancel an endpoint call if it exceeded some time. You can do it using a timed CancellationTokenSource like this. Note that the C# SDKs generated by APIMatic already have support for timeouts which can be configured in the client; this example is just for demonstration purpose:

// Set CancellationToken to fire after 2 seconds.
var cancellationTokenSource = new CancellationTokenSource(2000);
var cancellationToken = cancellationTokenSource.Token;

try {
// Call the endpoint with a CancellationToken as the last argument.
var response = await api.GetDataAsync(cancellationToken);
}
catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken)
{
// OperationCanceledException is thrown if the API call does not complete
// within 2 seconds.
}

Checkout these recommended patterns for using CancellationToken.

Concurrency Improvements in Java

As part of updating the generated Java SDKs to support Java 8, we also replaced the usage of callbacks in async endpoint methods with CompleteableFuture which will help the SDK users write better concurrent code. The CompleteableFuture instance returned by the async endpoint method resolves to the response expected from the endpoint.

Without CompleteableFuture, asynchronous actions have to be represented as callbacks which tend to be scattered across the code or deeply nested inside one another. CompleteableFuture introduced in Java 8 makes it possible to replace callbacks with a cleaner interface that resembles a more powerful form of the Promise interface seen in JavaScript code: the new interface contains over 50 methods for composing, combining, and executing asynchronous computation steps and handling errors!

This means that the Java SDK users can now chain API calls together, wait on multiple API calls and handle more advanced use-cases when calling the async endpoint methods. Checkout this code sample that shows the usage of the generated Java SDK with the new CompleteableFuture:

// Making an endpoint call and handling the response asynchronously.
api.sendDataAsync(request).thenAccept(result -> {
// Do something in case of a successful response...
}).exceptionally(exception -> {
// Do something in case of an error...
return null;
});

// You can also chain two endpoint calls together.
api.getDataAsync(request)
.thenCompose(
firstApiCallResponse -> api.sendDataAsync(firstApiCallResponse.SomeData))
.thenAccept(secondApiCallResponse -> {
// Do something in case of a successful response...
}).exceptionally(exception -> {
// Do something in case of an error...
return null;
});

Check out this article for more patterns on using CompleteableFuture.

Sending Binary and JSON-Encoded Content in Multipart Requests

Sending JSON-encoded content in a multipart request is now supported in all languages. Previously, we only supported sending binary data in the form of files but now you can also send JSON-encoded data by setting the encoding of the parameter as JSON in your API definition.

For endpoints that require sending a "binary" content or file in a multipart request, the generated SDKs now expect an instance of the "File Wrapper" to be passed in. The semantics for this depends on the language. The "File Wrapper" allows you to pass the file (or stream) instance, file name and content (or mime) type for the file in one go.

Here's a quick glimpse at how you would create a JSON-encoded and a "File" parameter in an endpoint in your OpenAPI definition:

"post": {
...
"operationId": "sendMultipart",
"summary": "This endpoint requires sending a JSON object and a file in a multipart request.",
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"request": {
"$ref": "#/components/schemas/ObjectThatGetsSentAsJson"
},
"file": {
"type": "string",
"format": "binary"
}
}
},
"encoding": {
"request": {
"contentType": "application/json; charset=utf-8"
},
"file": {
"contentType": "image/jpeg"
}
}
}
}
},
}

Using Simple Types Instead of Enumerations

The default behavior of the Code Generator is to create an enumeration type in the SDK for every enumeration type it encounters in the API definition. You can now configure the Code Generator to use simple types like strings or integers in the generated Java or C# SDK instead of using enumeration types.

Since enumerations are "closed types", they restrict the API server from sending some other string or number which is not present in the enumeration; this can cause a deserialization error when using the SDK because the API server would be breaking the contract defined in the API definition. This is the right behavior for most of the cases as it helps catch schema validation issues while also helping the SDK user write good code when using the SDK.

By setting the "Generate Enums" CodeGen Setting to false, you can change the generated SDK to use the simpler string or number type in place of enumeration types. This is useful in case you want to treat the enumerations defined in your API definition as "open types" or in case you intend to change the definition of the enumerations frequently over the course of the API lifetime.

// SDK usage when enumerations are generated...
SampleModel modelInstance = new SampleModel(OperationType.SUM, 2, 3);

// SDK usage when enumerations are NOT generated...
SampleModel modelInstance = new SampleModel("sum", 2, 3);

User-Friendly String Representation for Classes

The generated Java and C# SDKs now provide a convenient string representation of classes by overriding the default "stringification" method.

By overriding the toString (or in case of C#, the ToString) methods in the model, HTTP Request, HTTP Response and other utility classes generated with the SDKs, we allow the SDK user to quickly inspect these classes by printing them to the console.

// Create a sample model and write it to the standard output
SampleModel modelInstance = new SampleModel(OperationType.SUM, 2, 3);
System.out.println(modelInstance);

// Output: SampleModel [operation=SUM, x=2.0, y=3.0]

URL-Encoding of Template Parameters

By default, the generated SDKs will encode all template parameters in the endpoints using URL-encoding before the final URL for the endpoint is built. This makes it easy for the user of the SDK to provide data without having to URL-encode the template arguments in case it is needed.

However, this created a limitation where a URL path for an endpoint could not be dynamically built from the SDK user input because the template parameters were always URL-encoded, which would replace forward-slashes and other characters otherwise allowed in a path with their escaped forms.

We have now added a new CodeGen Setting called "Encode Template Parameters" which can be set to false to disable the URL-encoding behavior for all endpoints in the generated SDK. Note that this should be used carefully as our default behavior is often the right one in most cases.

note

The "Encode Template Parameters" is applied globally to all template parameters in all endpoints in the generated SDK. We are working on a setting that will allow this to be set for individual endpoint parameters instead. Please reach out to the support in case you are blocked by this.

Custom License Text

APIMatic Code Generator includes a license file in the source code of the generated SDKs. This defaults to the MIT license (in case you are a paid APIMatic user).

You can now define your custom license text by setting the "License Text" CodeGen Setting. The contents of this setting will be put in the LICENSE file in the generated SDK.

Improved Documentation in the SDKs

The SDKs generated by APIMatic come with complete documentation of the types and the methods available in the SDK. This documentation was included as a Markdown file named README.md in the SDK. We have updated this documentation to make it a more complete reference for the generated SDKs:

  • The documentation is now split into multiple pages by default. This helps overcome a limitation in GitHub (and other source code hosting platforms) that resulted in the README.md file being cut off because it was too long to display on one page. In addition to the README.md file, there is now an additional docs directory inside the generated SDKs that contains the parts of the documentation which are linked on the main README.md page.
  • We have updated the instructions for installing the SDK, initializing the client, setting the configuration and making the first API call in addition to updates to the API and Model Reference sections. This follows from the updates in the design of the SDK and the feedback from our users.
  • The endpoint documentation now shows detailed code samples on how to make the call along with sample request data that is derived from the examples given in the API definition file.
  • We have added documentation for the utility and infrastructure classes included in the SDK such as the HTTP Request, HTTP Response, Configuration and API Exception classes.

We have also added some new Code Generation settings for customizing the documentation:

  • Generate Examples For Optional Fields: This setting which is enabled by default ensures that the optional properties in the models are also included in the examples that are generated. Previously, optional properties were skipped for performance reasons.
  • Usage Example Endpoint: This setting lets you select the endpoint to use in the example shown in the initialization section of the documentation. Previously, the first endpoint in the API definition was used. This setting lets you use the endpoint that might make the most sense in the context of your API.
  • Is Latest Version: This setting removes the version number from the package installation instructions for the SDK. By default, we include the version number for the package in the package installation instructions in the generated documentation.