The Round Trip Problem

Consider a new feature added to the Product API version 1.1.0. For example, version 1.1.0 may add a property guaranteeType to the product, createProduct, and updateProduct schemas. Thus, the corresponding Product, CreateProduct and UpdateProduct classes in the SDK for API version 1.1.0 will also define guaranteeType. Apiture Open Banking APIs account for clients that are still using the older SDK for Product API 1.0.0, even though the API is now running 1.1.0. The TypeScript SDK for the Product API 1.0.0 includes TypeScript classes for 1.0.0 which do not include guaranteeType. If the client receives an product response or is passed a JSON representation of a product instance, it will contain the new property:

{
  "_profile": "https://api.apiture.com/schemas/products/product/v1.1.0/profile.json",
  "guaranteeType": "fdic",
  "..." : " ... other properties ..."
}

When the client (via the SDK) marshals that JSON into a Product 1.0.0 object, the guaranteeType property is dropped because the Product class has has no such property.

The same would happen in other client programming languages, such as a class named Product in a Java SDK.

If the client modifies the representation, then submits a PUT request, the reverse object mapping occurs: the client must convert the UpdateProduct object to JSON. However, when using SDK 1.0.0, the UpdateProduct class and schema will not contain guaranteeType property. Without version information in the request, a PUT update will change the persisted value of the guaranteeType property for that product instance from "fdic" to null. (This is the defined behavior of PUT operations: PUT is always a complete replacement of the resource representation.) In the absence of any version information in the request, if guaranteeType is not present in the request, the server would not know the difference between an older 1.0.0 client invoking the PUT and a 1.1.0 client attempting to remove the value of guaranteeType; it would change the persistent value of guaranteeType to null.

This is the Round Trip Problem with GET and PUT.

This round trip problem only occurs for clients which map JSON to strongly typed objects (programming languages objects, not JSON objects), manipulate the objects, and then map objects back to JSON. Round tripping is not an issue when clients operate on JSON objects directly instead of marshaling the JSON into and out of native objects. In this case, the guaranteeType property remains in the client’s JSON object and is returned in the PUT request body, even though the client is unaware of it.

PATCH operations do not have this round trip problem, as values that are omitted from the PATCH request body are ignored and do not change the value on the server. That is, the behavior of PATCH works whether guaranteeType was intentionally omitted in the request body, or because the 1.0.0 SDK does not have a corresponding property.

To solve this when using the SDK or marshaling, the Apiture Open Banking API SDK does the following two operations:

  • The SDK injects the correct _profile in the representation. The _profile URL includes to the resource version associated with API version for that SDK - i.e. it identifies the Product instance version as 1.0.0. If _profile was omitted, the resource would is interpreted as version 1.1.0 of the Product schema, and the PUT request would clear out the value of guaranteeType rather than leave it unmodified. The SDK constructs the new request body with an accurate _profile value associated with the SDK version, and thus it does not round-trip the _profile in the input.
  • The SDK for Product 1.0.0 passes the Apiture-Version: 1.0.0 header in the request.
  • The SDK passes the API key request header; this value is also a surrogate for the Apiture-Version.

If a client does not use the SDK, it should account for the round trip problem in the same way: include the _profile that reflects the expected version number of the resource, and pass the correct Apiture-Version header corresponding to the schema version associated with the resource and the client’s API key.