In a nut shell, when we send a request to a routed method, an API version object that includes a required API version is also sent along within the request. A new object called "versionedmethod" is going to be created that takes the API version objects (min_version, max_version) as well as the routed method. A new, dynamic mapping called "versioned_methods" is created to store the "versionedmethod"s and it automatically checks and returns the match one. The below information is the summary of each element design in the overall design of microversion.

This object will get the mis_version, max_version from headers provided in API.

Class Version(object):
	def __init__(self, headers, min_ver, max_ver):
		raise NotImplementedError
	def __eq__(self, other):
	def __lt__(self, other):
	def __gt__(self, other):
 	def matches(self, start_version, end_version):
Class VersionedMethod(object):
	def __init__(self, name, start_version, end_version, function):
		raise NotImplementedError
class Controller():
	def api_version(cls, min_ver, max_ver=None):
		def decorator(f):
			return f
		return decorator

In the routed controller, for each method, we need to put the decorator of api_version() at the most outer as showed as below example:

from pecan import expose
class RoutedController(Controller):
	@Controller.api_version(1.1, 1.10)
	def post():
		return This method supports version from 1.1 to 1.10
	def post():
		return This method only supports version 1.11

The base controller is designed to automatically check the version of exposed methods in request and return the match one based. For example, in the RoutedController, we support two post() method, each of them supports a range of versions. When we route a request to the POST method companying with a specific version, the base controller will collect the number of post() method that request may be routed to and choose the right one that fits the requirement of version.

In this design, we create a parameter so called versioned_methods. The requirement of this parameter is that the value of versioned_methods that contain the number of routed methods needs to be automatically, dynamically changed, therefore we implement a built-in getattribute method along with metaclass. The reason why we use metaclass is deeply explained later.

Besides, as we mentioned above that the version of each method will be implemented as an object and sent along with request, in the base controller, we will take it out from request and execute the comparison operation.

NOTE: Why do we implement metaclass here?

As we know that metaclass is used in API implementation (check the Django ORM source code), metaclass is creating classes therefore all the members/instances/inheritances get possibility to customize the classes. So that metaclass is used when we want to implement the automatic modification methods of class inheritances to class attributes.

In the above implementation, we see that, we need to modify the value of versioned_methods (it is a mapping) of class Controller() whenever there is a function that is decorated with api_version(). This versioned_methods mapping maps the name of each versioned_method with number of its versioned object. Since the decorator api_version() is used in multiple times with multiple methods, the value of versioned_methods has to have ability to automatically change. This is the result of implementing metaclass here.


This method uses version 1.6 of API, It will return the result: “This method supports version from 1.1 to 1.10”

curl -i -H "Accept:application/json" -H "X-Vietstack-Api-Version: Microversion 1.6" -X POST -H "Content-Type: application/json" -d ‘’

This method uses version 1.11 of API, It will return the result: “This method only supports version 1.11”

curl -i -H "Accept:application/json" -H "X-Vietstack-Api-Version: Microversion 1.11" -X POST -H "Content-Type: application/json" -d ‘’




VietStack team