Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django-rest-framework: api versioning

so googling around it appears that the general consensus is that embedding version numbers in REST URIs is a bad practice and a bad idea.

even on SO there are strong proponents supporting this.
e.g. Best practices for API versioning?

My question is about how to accomplish the proposed solution of using the accept header / content negotiation in the django-rest-framework to accomplish this.

It looks like content negotiation in the framework,
http://django-rest-framework.org/api-guide/content-negotiation/ is already configured to automatically return intended values based on accepted MIME types. If I start using the Accept header for custom types, I'll lose this benefit of the framework.

Is there a better way to accomplish this in the framework?

like image 502
w-- Avatar asked Jan 11 '13 00:01

w--


People also ask

What is versioning in Django REST framework?

API versioning allows you to alter behavior between different clients. REST framework provides for a number of different versioning schemes. Versioning is determined by the incoming client request, and may either be based on the request URL, or based on the request headers.

What is REST API versioning?

API versioning is the practice of transparently managing changes to your API. Managing an API boils down to defining and evolving data contracts and dealing with breaking changes. The most effective way to evolve your API without breaking changes is to follow effective API change management principles.

Is Django GOOD FOR REST API?

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django.


1 Answers

UPDATE:

versioning is now properly supported.


There are some answers from your link:

We found it practical and useful to put the version in the URL. It makes it easy to tell what you're using at a glance. We do alias /foo to /foo/(latest versions) for ease of use, shorter / cleaner URLs, etc, as the accepted answer suggests. Keeping backwards compatibility forever is often cost-prohibitive and/or very difficult. We prefer to give advanced notice of deprecation, redirects like suggested here, docs, and other mechanisms.

So we took this approach, plus allowing clients to specify the version in request header (X-Version), here is how we did it:

Structure in side the API app:

. ├── __init__.py ├── middlewares.py ├── urls.py ├── v1 │   ├── __init__.py │   ├── account │   │   ├── __init__.py │   │   ├── serializers.py │   │   └── views.py │   └── urls.py └── v2     ├── __init__.py     ├── account     │   ├── __init__.py     │   ├── serializers.py     │   └── views.py     └── urls.py 

project urls.py:

url(r'^api/', include('project.api.urls', namespace='api')), 

api app level urls.py:

from django.conf.urls import *  urlpatterns = patterns('',     url(r'', include('project.api.v2.urls', namespace='default')),     url(r'^v1/', include('project.api.v1.urls', namespace='v1')), ) 

version level urls.py

from django.conf.urls import * from .account import views as account_views from rest_framework.routers import DefaultRouter  router = DefaultRouter() router.register('account', account_views.AccountView) router.register('myaccount', account_views.MyAccountView) urlpatterns = router.urls 

create a middleware to switch to the correct code by changing the path_info, please note there is a caveat that namespace ('api') defined in project level urls is not flexible and needs to be known in middleware:

from django.core.urlresolvers import resolve from django.core.urlresolvers import reverse   class VersionSwitch(object):      def process_request(self, request):         r = resolve(request.path_info)         version = request.META.get('HTTP_X_VERSION', False)         if r.namespace.startswith('api:') and version:             old_version = r.namespace.split(':')[-1]             request.path_info = reverse('{}:{}'.format(r.namespace.replace(old_version, version), r.url_name), args=r.args, kwargs=r.kwargs) 

Sample url:

curl -H "X-Version: v1" http://your.domain:8000/api/myaccount/ 
like image 125
James Lin Avatar answered Oct 03 '22 05:10

James Lin