As a seasoned API developer, you end up doing very repetitive tasks so you might be looking for tools that makes your development time faster. As a novice, you might be looking for a way to implement best practice and REST standards easily out of the box without too much hesitation.

In both cases, Django Rest Framework (DRF) is a great solution. It is a standard, widely used, and fully featured API framework that will not only save you a lot of time but also show you the right way to develop RESTful APIs. More particularly, DRF proposes generic views, that’s to say pre-built endpoints for your API. Let’s see how to leverage this feature to achieve rapid API development.

I put the below code in a little working Django project right here.

Concept

DRF’s Generic views are perfect for simple APIs that basically do CRUD (create, read, update, delete) on the database without too much data processing. For example, let’s say you have a product table that contains all your store products and you want to expose these products as is to customers through an API, then it’s a perfect use case for the ListAPIView (see below).

From now on I’m assuming that you installed Python, Django, DRF and that you know tha basics about Django.

Basic Example 1: Reading Data

Let’s create an API endpoint showing all the products customers. In your views.py do the following:

from rest_framework import generics
from .serializers import ProductsSerializer

class GetProducts(generics.ListAPIView):
    """Return all products."""
    serializer_class = ProductsSerializer

ProductsSerializer is the serializer that will convert your data from the database to API friendly data. This serializer should be put in serializers.py and will be in charge of retrieving data from your Product model and transforming them:

from rest_framework import serializers
from .models import Product

class ProductsSerializer(serializers.ModelSerializer):
    """Serialize products."""

    class Meta:
        model = Product
        fields = ("__all__")

Now in your urls.py create the route to this endpoint:

from django.urls import path
from .views import GetProducts

urlpatterns = [
    path('get-products/', GetProducts.as_view(), name='get_products'),
]

As you can see this is dead simple as DRF is doing many things for you under the hoods! You now have an endpoint (/get-products/) that you can consume with get HTTP requests, and that outputs all products with an API format (usually json but it depends on your settings).

Basic Example 2: Deleting Data

Now let’s create an endpoint dedicated to deleting a product for authenticated users only. It’s even simpler as it does not require to serialize data (once the product is deleted no data can be returned to user anymore).

In views.py:

from rest_framework import generics

class DeleteProduct(generics.DestroyAPIView):
    """Remove product"""
    permission_classes = (permissions.IsAuthenticated,) # Limit to authenticated users only

In urls.py

from django.urls import path
from .views import DeleteProduct

urlpatterns = [
    path('delete-product/', DeleteProduct.as_view(), name='delete_product'),
]

Now you have a /delete-product/ endpoint that you can use to delete one product at a time using delete HTTP requests, and that only accepts authenticated requests (authentication mechanism depends on your settings).

Customizing Generic Views’ Behavior

Each generic view can be customized by writing a get_queryset() method. For example let’s say you only want to show products that have an active flag set to True in db. You could do this:

from rest_framework import generics
from .serializers import ProductsSerializer
from .model import Product

class GetProducts(generics.ListAPIView):
    """Return all active products."""
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = ProductsSerializer

    def get_queryset(self):
        """Filter active products."""
        return Product.objects.filter(active=True)

get_queryset() is a common method that you have in every generic views. Some generic views also have their own methods to control more precisely the behavior of the endpoint. For example, let’s say that you don’t really want to delete products but just mark them as inactive. You could use the destroy() method:

from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework import status

class DeleteProduct(generics.DestroyAPIView):
    """Remove product"""
    permission_classes = (permissions.IsAuthenticated,)

    def destroy(self, request, pk):
        """
        By default, DestroyAPIView deletes the product from db.
        Here we only want to flag it as inactive.
        """
        product = get_object_or_404(self.get_queryset(), pk=pk)
        product.active = False
        product.save()
        return Response(status=status.HTTP_204_NO_CONTENT)

In the above example we’re first trying to look for the product that the user wants to delete. If we can’t find it we return a 404 code to user. If the product is successfully marked as inactive, we just return a 204 code to user meaning that the product was successfully deleted.

Generic views are perfect for simple use cases and it’s sometimes wiser to use the classic APIView for edge cases. For example, let’s say you want not only to return products to the user but also enrich data with other information that is not in the Product model (e.g. orders related to this product, product manufacturer, etc.). In that case, if you wanted to use generic views, you would have to define new fields in the serializer thanks to additional get_new_field() methods which can easily make your serializer very ugly…

Conclusion

As you could see, DRF’s generic views make API endpoints development very easy thanks to a bit of magic under the hood. However you should keep in mind that generic views cannot apply to every use cases as sometimes tweaking generic views is harder than developing things by yourself from scratch!

I hope you liked this little how to. I’d love to have your feedbacks!

Existe aussi en français

API Rate Limiting With Traefik, Docker, Go, and Caching

Limiting API usage based on advanced rate limiting rule is not so easy. In order to achieve this behind the NLP Cloud API, we're using a combination of Docker, Traefik (as a reverse proxy) and local caching within a Go script. When done correctly, you can considerably improve the performance of your rate limiting and properly throttle API requests without sacrificing speed of the requests. Continue reading