Search code examples
databaseapirestversioningdatabase-versioning

API Versioning and Storing Data


When exposing different API versions how do you handle storing and retrieving data that might have different structures?

Let's say we have two API versions; V1 and V2. V1 and V2 both have a POST endpoint at 'https://api.com/message' which will create a message in the database based on the passed data like:

{
    DOB: '2014-12-01'
}

In V1, the data required is different from V2 because in V2 we decide to change DOB from a string with the format 'YYYY-MM-DD' to an integer timestamp e.g. 1284723728323

In this case when we save data from a call with the V2 API the DOB field will be an integer but when saving from a call to V1 it will be a string in a very different format.

With each iteration of the API we might modify many aspects of the underlying data. Calling older API versions will result in the stored data being incorrect for other versions of the API.

Is there an elegant way to deal with different API versions requiring data in different formats / structures?


Solution

  • What I would do first of all is to make sure that each web-facing API corresponds to an actual interface in a real programming language (say, Java.)

    So, the problem now becomes a problem of programming-language API versioning, and is free from considerations that are specific to web services.

    Then, I would make the name of the interface contain the number of the version, as in MyWebServiceInterfaceV1.

    Once a version of the interface is released to the outside world, ("it is free in the wild", so to speak,) the source code folder containing the interface gets locked in the source code repository, so that nobody can modify it again, ever. Then you make a copy of the interface and you increment its version number, and from that moment on, all new work is done on the new version.

    So, we are now working on MyWebServiceInterfaceV2.

    With every new version of the interface that you introduce, you also write a converter class which implements the old interface and maps to the new, and you keep maintaining that class until the new interface gets locked too.

    So, the converter class from V1 to V2 would have to be able to convert string-dates to integer-dates and possibly also the reverse: integer-dates to string-dates. The important thing to realize here is that all conversions should be possible; if a conversion seems impossible, then this means that you are planning to make a change in a direction which will make your system not backwards compatible with older versions. For as long as you keep backwards compatibility in mind in all new designs, all necessary conversions should remain possible.

    If you have N versions you can either implement (N-squared)/2 different converters so as to be able to directly convert from any version to any newer version, or you can have only N converters, each one of them only capable of converting between two successive versions, and stack as many of them as necessary in order to go from a very old version step by step to the newest version.

    Of course, at any given time, only the latest version of the interface is backed by an actual implementation which communicates to the actual database.