drf
A quick Google search for “REST API versioning” turns up lots of discussion around how a version should be specified in the request. There is no shortage of passionate debate around RESTful principles and the pros and cons of whether embedding version numbers in urls, using custom request headers, or leveraging the existing accept header is the best way to go. Unfortunately, there isn’t any one correct answer that will satisfy everyone. Regardless of which approach you end up using, some camp will proclaim that you are doing it wrong. Respective of the approach that is used, once the version is parsed out of the request, there is another, larger, question around how you should manage your API code to support the different schema’s used across different versions. Surprisingly, there is little discussion out there around how to best accomplish this.

At Rescale, we have built our API with the Django Rest Framework. The upcoming 3.1 release will offer some API versioning support. The project lead has wisely decided to sidestep the versioning debate by providing a framework that allows API builders to select between different strategies for specifying the version in the request. However, there isn’t much official guidance around what the best practices are for dealing with this version in your code. Indeed, the documentation mostly just punts on the issue by stating “how you vary your behavior is up to you”.

One of Stripe’s engineers posted a nice high-level summary of how Stripe deals with backwards compatibility with their API. The author describes a transformation pipeline that requests and responses are passed through. One of the main appeals of this approach is that the core API logic is always dealing with the current version of the API and there are separate “compatibility” layers to deal with the different versions. Requests from the client pass through a “request compatibility” layer to transform them into the current schema before being handed off to the core API logic. Responses from the core API logic are passed through a “response compatibility” layer to downgrade the response into the schema format of the requested version.

In this post, I want to explore a potential approach for supporting this type of transformation pipeline with DRF. The natural place then to inject the transform logic in DRF is within the Serializer as it is involved in both validating the request data (Serializer.to_internal_value) as well as preparing response data to return from an APIView method (Serializer.to_representation).

The general idea is to create an ordered series of transformations that will be applied to request data to convert it into the current schema. Similarly, response data will be passed through the transformations in the reverse order to convert data in the current schema to the version requested by the client. This ends up looking very similar to the forwards and backwards methods on database migrations.

As a basic example of how a response transform would be used, the following is a simple serializer that returns back a mailing list name and list of subscriber emails:

The payload from an endpoint that uses this serializer might return JSON formatted as:

Some time later, we decide that this endpoint also needs to return the date that each subscriber signed up for the mailing list. This is going to be a breaking change for any client that is using the original version of the API as each element in the subscribers array is now going to be an object instead of a simple string:

The serializer needs to updated to return data in this new format. To support backwards compatibility with the original version of the API, it will also need to be modified to derive from a VersioningMixin class and specify the location of the Transform classes (more on this in a bit):

Whenever a new API version is introduced for this serializer, a new numbered Transform class needs to be added to the api.transforms.mailinglist module. Each Transform handles the the downgrade of version N to version N-1 by munging the response data dict:

The Transform class is the analogue of a schema migration and contains methods to transform request and response data. Each Transform class name needs to have a numerical value as a suffix. The VersioningMixin class uses this to identify the order that Transforms should be applied to the request or response data.

The VerisoningMixin class provides the Serializer.to_internal_value and Serializer.to_representation overrides that will look up the Transforms pointed to by the transform_base property on the serializer and apply them in order to convert requests into the current API version or downgrade responses from the current API version to the requested version. In the following code snippet, settings.API_VERSION refers to the latest, current API verison number and the request.version field is set to the requested API version from the client:

The main benefit of this approach is the  APIView (the classes that will generally implement the core API logic and use Serializers for request/response processing) only need to worry about the latest schema version. In addition, writing a Transform requires knowledge of the only current and previous version of the API. When creating version 10 of a particular response, there is just a single Transform between v10 and v9 that needs to be created. A request asking for v7, will be first transformed from v10 to v9 by the new Transform. The existing v9 to v8 and v8 to v7 Transforms will handle the rest.

We certainly do not believe that this is a panacea for all backwards compatibility issues that will crop up. There are certainly some performance issues to consider with having to constantly run requests and responses through a series of potentially expensive transformations. Further, in the same way that it is sometimes impossible to create backwards migrations for database schema changes, there are certainly more complex breaking API changes that are not easily resolvable by this approach. However, for basic API changes, this seems like it could be a nice way to isolate concerns and avoid embedding conditional goop inside the core API logic for versioning purposes.

This article was written by Ryan Kaneshiro.

react

React is a declarative framework that allows us to describe how our components should look at any given point in time and will manage all the UI updates when the underlying data changes. If we follow best practices for managing state, such as keeping around only the minimal set of state needed for the UI and computing everything else on demand, we generally write a lot less imperative code. Following this practice also has the benefit of eliminating an entire class of bugs related to inconsistent UI state. However, there are situations where properties of a component’s state can depend on each other in a way that one isn’t always computed from another, and intuitive attempts to keep state within these constraints can introduce dreaded imperative code. This article will demonstrate such a scenario and suggest a more declarative way of keeping state consistent.

The Example

We want a component that renders a list of items. We can select an item to toggle its checked state. If any item is checked, the component will show a “Selected” tab that, when clicked, shows only checked items. We implement this with a component that has two state properties. The first is checkedSet which holds a set of the checked items, and the second is onlyShowChecked. When the latter property is true the component shows only checked item and the Selected tab is “active”. Sounds simple, here’s the code:

JS Bin

Looks good, but there’s an undesirable state. When we’re in the Selected tab and uncheck all the items, our UI isn’t showing any items and there isn’t an active tab anymore. We’d probably want to fix this by automatically switching back to the show all tab whenever we uncheck the last item. The most obvious place to implement that behavior is of course in the action that got us into this state. That is, in toggleChecked we’d want to check whether we’ve just unchecked the last item and if so set onlyShowChecked to false. Our modified toggleChecked may look something like this:

Sure enough, replay that scenario and it’ll work as desired: JS Bin

However, there are a couple things wrong with this approach. The toggleChecked function now has multiple responsibilities. In addition to toggling the checked state of an item, it now also sets the onlyShowChecked property. The next developer reading this code won’t understand it at a glance, compared to the previous version. They will have to think about why this check was introduced and will always need to keep this edge case in their head when modifying or adding features.

Now consider what can happen when we add a button to uncheck everything at once. This button calls the uncheckAll function, which clears checkedSet:

JS Bin

Indeed, it’s again possible to get into that unwanted state since we can use this new button to uncheck all the items while in the Selected tab. At this point it’s considered a bug and there are a couple of intuitive ways to fix this.

One obvious fix is to inline the same check that we introduced to toggleChecked, but again this would add to the responsibility of uncheckAll and duplicating logic is never good for maintainability.

Another way is to loop over the checked items and call toggleChecked on each one. This would work, but it would feel like we’re doing much more work then we need to. And who can predict the rendering behavior when calling setState that many times in a function call? Also the only reason we would even consider doing it this way is because we know toggleChecked has the side effect that we want, and this would tightly couple the two functions together.

Either way, let’s say a couple of months from now we want to introduce a third way of toggling items, say by adding a button that inverts the checked state of each item. This is effectively an uncheck all if all items are checked. At this point it’s natural to forget that we’ve been adding code to prevent our component from getting into an undesirable state and reintroducing the bug is all too easy.

The fundamental problem is that there isn’t an obvious way in React to declaratively describe the dependencies between our state properties. That is, in our case, onlyShowChecked should never be true when checkedSet is empty. So, in order to maintain that invariant, we end up writing imperative state manipulations reminiscent of the imperative DOM manipulations that we used to write with jQuery, which will inevitably lead to maintainability problems down the road. This is what I call imperative creep.

In Angular, or another framework that provides observables, we can just watch the checkedSet property in order to keep showSelected in the correct state. The best way we’ve found to manage dependent state in React is by mutating the state directly at the very top of the render function:

Now we admit this looks like bad practice in any React application, but we didn’t come to this conclusion lightly. We’ve tried managing state in the shouldComponentUpdate lifecycle method, but that introduced a bunch of incompatibilities such as when using a mixin that already provides shouldComponentUpdate, or when state is an Immutable instead of a plain javascript object, or having forceUpdate completely bypass shouldComponentUpdate.

By managing dependent state at the top of a render function we don’t have to deal with these incompatibilities. The benefits are that we’ve eliminated the root cause of our UI bug and it encourages developers to keep all the rules for dependent state properties in one place.

This article was written by Kenneth Chung.