API Star: Python 3 API Framework

For building quick APIs in Python, I have mostly depended on Flask. Recently I came across a new API framework for Python 3 named “API Star” which seemed really interesting to me for several reasons. Firstly the framework embraces modern Python features like type hints and asyncio. And then it goes ahead and uses these features to provide awesome development experience for us, the developers. We will get into those features soon but before we begin, I would like to thank Tom Christie for all the work he has put into Django REST Framework and now API Star.

Now back to API Star – I feel very productive in the framework. I can choose to write async codes based on asyncio or I can choose a traditional backend like WSGI. It comes with a command line tool –¬†apistar¬†to help us get things done faster. There’s (optional) support for both Django ORM and SQLAlchemy.¬† There’s a brilliant type system that enables us to define constraints on our input and output and from these, API Star can auto generate api schemas (and docs), provide validation and serialization feature and a lot more. Although API Star is heavily focused on building APIs, you can also build web applications on top of it fairly easily. All these might not make proper sense until we build something all by ourselves.

Getting Started

We will start by installing API Star. It would be a good idea to create a virtual environment for this exercise. If you don’t know how to create a virtualenv, don’t worry and go ahead.

pip install apistar

If you’re not using a virtual environment or the pip¬†command for your Python 3 is called pip3, then please use pip3 install apistar¬†instead.

Once we have the package installed, we should have access to the apistar¬†command line tool. We can create a new project with it. Let’s create a new project in our current directory.

apistar new .

Now we should have two files created – app.py¬†– which contains the main application and then test.py¬†for our tests. Let’s examine our app.py¬†file:

from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar.handlers import docs_urls, static_urls


def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}


routes = [
    Route('/', 'GET', welcome),
    Include('/docs', docs_urls),
    Include('/static', static_urls)
]

app = App(routes=routes)


if __name__ == '__main__':
    app.main()

Before we dive into the code, let’s run the app and see if it works. If we navigate to http://127.0.0.1:8080/¬†we will get this following response:

{"message": "Welcome to API Star!"}

And if we navigate to: http://127.0.0.1:8080/?name=masnun

{"message": "Welcome to API Star, masnun!"}

Similarly if we navigate to: http://127.0.0.1:8080/docs/, we will see auto generated docs for our API.

Now let’s look at the code. We have a welcome¬†function that takes a parameter named name¬†which has a default value of None. API Star is a smart api framework. It will try to find the name¬†key in the url path or query string and pass it to our function. It also generates the API docs based on it. Pretty nice, no?

We then create a list of Route and Include instances and pass the list to the App instance. Route objects are used to define custom user routing. Include , as the name suggests, includes/embeds other routes under the path provided to it.

Routing

Routing is simple. When constructing the App instance, we need to pass a list as the routes argument. This list should comprise of Route or Include objects as we just saw above. For Routes, we pass a url path, http method name and the request handler callable (function or otherwise). For the Include instances, we pass a url path and a list of Routes instance.

Path Parameters

We can put a name inside curly braces to declare a url path parameter. For example /user/{user_id}¬†defines a path where the user_id¬†is a path parameter or a¬† variable which will be injected into the handler function (actually callable). Here’s a quick example:

from apistar import Route
from apistar.frameworks.wsgi import WSGIApp as App


def user_profile(user_id: int):
    return {'message': 'Your profile id is: {}'.format(user_id)}


routes = [
    Route('/user/{user_id}', 'GET', user_profile),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

If we visit http://127.0.0.1:8080/user/23 we will get a response like this:

{"message": "Your profile id is: 23"}

But if we try to visit http://127.0.0.1:8080/user/some_string¬†– it will not match. Because the user_profile¬†function we defined, we added a type hint for the user_id¬†parameter. If it’s not integer, the path doesn’t match. But if we go ahead and delete the type hint and just use user_profile(user_id), it will match this url. This is again API Star is being smart and taking advantages of typing.

Including / Grouping Routes

Sometimes it might make sense to group certain urls together. Say we have a user module that deals with user related functionality. It might be better to group all the user related endpoints under the /user path. For example Р/user/new, /user/1, /user/1/update and what not. We can easily create our handlers and routes in a separate module or package even and then include them in our own routes.

Let’s create a new module named user, the file name would be user.py. Let’s put these codes in this file:

from apistar import Route


def user_new():
    return {"message": "Create a new user"}


def user_update(user_id: int):
    return {"message": "Update user #{}".format(user_id)}


def user_profile(user_id: int):
    return {"message": "User Profile for: {}".format(user_id)}


user_routes = [
    Route("/new", "GET", user_new),
    Route("/{user_id}/update", "GET", user_update),
    Route("/{user_id}/profile", "GET", user_profile),
]

Now we can import our user_routes from within our main app file and use it like this:

from apistar import Include
from apistar.frameworks.wsgi import WSGIApp as App

from user import user_routes

routes = [
    Include("/user", user_routes)
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

Now /user/new will delegate to user_new function.

Accessing Query String / Query Parameters

Any parameters passed in the query parameters can be injected directly into handler function. Say for the url /call?phone=1234, the handler function can define a phone¬†parameter and it will receive the value from the query string / query parameters. If the url query string doesn’t include a value for phone, it will get None¬†instead. We can also set a default value to the parameter like this:

def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}

In the above example, we set a default value to name which is None anyway.

Injecting Objects

By type hinting a request handler, we can have different objects injected into our views. Injecting request related objects can be helpful for accessing them directly from inside the handler. There are several built in objects in the http¬†package from API Star itself. We can also use it’s type system to create our own custom objects and have them injected into our functions. API Star also does data validation based on the constraints specified.

Let’s define our own User¬†type and have it injected in our request handler:

from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar import typesystem


class User(typesystem.Object):
    properties = {
        'name': typesystem.string(max_length=100),
        'email': typesystem.string(max_length=100),
        'age': typesystem.integer(maximum=100, minimum=18)
    }

    required = ["name", "age", "email"]


def new_user(user: User):
    return user


routes = [
    Route('/', 'POST', new_user),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

Now if we send this request:

curl -X POST \
  http://127.0.0.1:8080/ \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{"name": "masnun", "email": "[email protected]", "age": 12}'

Guess what happens? We get an error saying age must be equal to or greater than 18. The type system is allowing us intelligent data validation as well. If we enable the docs url, we will also get these parameters automatically documented there.

Sending a Response

If you have noticed so far, we can just pass a dictionary and it will be JSON encoded and returned by default. However, we can set the status code and any additional headers by using the Response¬†class from apistar. Here’s a quick example:

from apistar import Route, Response
from apistar.frameworks.wsgi import WSGIApp as App


def hello():
    return Response(
        content="Hello".encode("utf-8"),
        status=200,
        headers={"X-API-Framework": "API Star"},
        content_type="text/plain"
    )


routes = [
    Route('/', 'GET', hello),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

It should send a plain text response along with a custom header. Please note that the content¬†should be bytes, not string. That’s why I encoded it.

Moving On

I just walked through some of the features of API Star. There’s a lot more of cool stuff in API Star. I do recommend going through the Github Readme for learning more about different features offered by this excellent framework. I shall also try to cover short, focused tutorials on API Star in the coming days.

Deploying A Flask based REST API to AWS Lambda (Serverless) using Zappa

I have heard about AWS Lambda and all the cool things happening in the serverless world. I have also deployed Go functions using the Apex framework for serverless deployment. But recently I have started working on some Python projects again and decided to see how well the Python community is adapting to the serverless era. Not to my surprise, the Python community is doing great as usual. I quickly found an awesome framework named Zappa which makes deploying Python code to AWS Lambda very easy. Python is already natively supported on the AWS Lambda platform. But with the native support you need to configure the API Gateway, S3 bucket and other stuff on your own. Thanks to Zappa, these things are now automated to our convenience. We can easily deploy WSGI apps as well. That means, we can now take our Flask / Django / API Star apps and deploy them to AWS Lambda – with ease and simplicity. In this blog post, I will quickly walk through how to deploy a flask based rest api to the serverless cloud.

Setup Flask App

Before we can get started, we need to create a simple flask app. Here’s a quick rest api (that doesn’t do much):

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/')
def hello_world():
    return jsonify({"message": "Hello World!"})

Let’s save the above code in app.py. We can now install Flask using pip:

pip install flask

And then run the code:

FLASK_APP=app.py flask run

This should run our app and we should be able to visit http://127.0.0.1:5000/ to see the output.

Setting Up AWS

Please make sure you have an account for AWS where you have added your credit card and completed the sign up process. AWS Lambda has 1 million free requests per month which is promised to be always free (not for the first 12 months or anything). When you add your card, you shall also be eligible for a free tier of S3 for 12 months. So you won’t be charged for trying out a sample app deployment. So don’t worry about adding a card. In fact, adding a card is a requirement for getting the free tier.

Once you have your AWS account setup, click on your name (top right) and click “My Security Credentials”. From there, choose the “Access Keys” section and generate a new pair of key and secret. Store them in your ~/.aws/credentials¬†file. AWS Cli (and Zappa) will use these to connect to AWS services and perform required actions. The file should look like this:

[default]
aws_access_key_id=[...]
aws_secret_access_key=[...]

[masnun]
aws_access_key_id=[...]
aws_secret_access_key=[...]

I have created two profiles here. Named profiles are useful if you have more than one accounts/projects/environments to work with. After adding the credentials, add the region information in ~/.aws/config:

[default]
region=us-west-2
output=json

[profile masnun]
region=us-east-2
output=text

This will mostly help with choosing the default region for your app. Once you have these AWS settings configured, you can get started with Zappa.

Install and Configure Zappa

First install Zappa:

pip install zappa

Now cd into the project directory (where our flask app is). Then run:

zappa init

Zappa should guide you through the settings it needs. It should also detect the Flask app and auto complete the app (app.app¬†) for you. Once the wizard finishes, you’re ready to deploy your API.

zappa deploy dev

This should now deploy the app to the dev stage. You can configure different stages for your app in Zappa settings. Once you make some code changes, you can update the app:

zappa update

Both of these commands should print out the url for the app. In my case, the url is:¬†https://1gc1f80kb5.execute-api.us-east-2.amazonaws.com/dev¬†ūüôā

What’s Next?

Congratulations, you just deployed a Flask rest api to AWS Lambda using Zappa. You can make the url shorter by pointing a domain to it from your AWS console. To know more about Zappa and all the great things it can do, please check out Zappa on Github.

Django REST Framework: JSON Web Tokens (JWT)

(This post is a part of a tutorial series on Building REST APIs in Django)

Our last post was about Authentication and Permissions and we covered the available methods of authentication in Django REST Framework. In that post, we learned how to use the built in Token based authentication in DRF. In this post, we will learn more about JSON Web Tokens aka JWT and we will see if JWT can be a better authentication mechanism for securing our REST APIs.

Understanding JSON Web Tokens (JWTs)

We have actually written a detailed blog post about JSON Web Tokens earlier. In case you have missed it, you probably should read it first. We have also described how to use JWT with Flask – reading that one might also help better understand how things work. And of course, we will briefly cover the idea of JWT in this post as well.

If we want to put it simply – you take some data in JSON format, you hash it with a secret and you get a string that you use as a token. You (your web app actually) pass this token to the user when s/he logs in. The user takes the token and on subsequent requests, passes it back in the “Authorization” header. The web app now takes this token back, “decodes” it back to the original JSON payload. It can now read the stored data (identity of the user, token expiry and other data which was embedded in the JSON). While decoding, the same secret is used, so third party attackers can’t just forge a JWT. We would want our token to be small in size, so the JSON payload is usually intentionally kept small. And of course, it should not contain any sensitive information like user password.

JWT vs DRF’s Token Based Authentication

So in our last blog post, we saw Django REST Framework includes a token based authentication system which can generate a token for the user. That works fine, right? Why would we want to switch to JSON Web Tokens instead of that?

Let’s first see how DRF generates the tokens:

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

It’s just¬†random. The token generated can not be anyway related to the user that it belongs to. So how does it associate a token with an user? It stores the token and a reference to the user in a table in database. Here comes the first point – while using DRF’s token based auth, we need to query database on every request (unless of course we have cached that token which). But what if we have multiple application servers?¬†Now we need all our application servers to connect to the same database or same cache server. How will that scale when the project gets really really big? What if we want to provide single sign on across multiple services? We will need to maintain a central auth service where other services request to verify a token. Can JWT simplify these¬†for us?

JWT is just an encoded (read – hashed / signed) JSON data. As long as any webservice has access to the secret used in signing the data, it can also decode and read the embedded data. It doesn’t need any database calls. You can generate the token from one service and other services can read and verify it just fine. It’s more efficient and simply scales better.

JWT in Django REST Framework

DRF does not directly support JWTs out of the box. But there’s an excellent package that adds support for it. Let’s see how easily we can integrate JWT in our REST APIs.

Install and Configure

Let’s first install the package using pip¬†–

pip install djangorestframework-jwt

That should install the package. Now we need to add rest_framework_jwt.authentication.JSONWebTokenAuthentication to the default authentication classes in REST Framework settings.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}

We added it to the top of the list. Next, we just have to add it’s built in view to our urlpatterns.

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = router.urls + [
    url(r'^jwt-auth/', obtain_jwt_token),
]
Obtain a Token

The obtain_jwt_token¬†view will check the user credentials and provide a JWT if everything goes alright. Let’s try it.

$ curl --request POST \
  --url http://localhost:8000/api/jwt-auth/ \
  --header 'content-type: application/json' \
  --data '{"username": "test_user", "password": "awesomepwd"}'

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJlbWFpbCI6IiIsInVzZXJuYW1lIjoidGVzdF91c2VyIiwiZXhwIjoxNDk1OTkyOTg2fQ.sWSzdiBNNcXDqhcdcjWKjwpPsVV7tCIie-uit_Yz7W0"}

Awesome, everything worked just fine. We have got our token too. What do we do next? We use this token to access a secured resource.

Using the obtained JWT

We need to pass the token in the form of JWT <token>¬†as the value of the Authorization¬†header. Here’s a sample curl request:

$ curl -H "Content-Type: application/json" -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJlbWFpbCI6IiIsInVzZXJuYW1lIjoidGVzdF91c2VyIiwiZXhwIjoxNDk1OTkyOTg2fQ.sWSzdiBNNcXDqhcdcjWKjwpPsVV7tCIie-uit_Yz7W0" -X GET  http://localhost:8000/api/subscribers/

[{"id":1,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"},{"id":2,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"},{"id":3,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"},{"id":4,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"}]

So our token worked fine! Cool!

Where to go next?

Now that you have seen how simple and easy it is to add JSON Web Token based authentication to Django REST Framework, you probably should dive deeper into the package documentation. Specially these topics might be interesting –

  • Refresh Tokens: If you enable JWT token refreshing, you can exchange your current token with a new, fresh one before¬†the existing one expires. The new token will of course have a renewed expiry time set.
  • Verify Token: If you just share the secret, all services can verify the user on their own. However, in modern micro service based architecture, you may want to provide an API end point that other services can use to verify a JWT they received from the user. This can be useful for those scenarios.
  • And of course look at the settings options available¬†and see how you can customize the token generation process.

In the future, we shall try to cover more about Django, Django REST Framework and Python in general. If you liked the content, please subscribe to the mailing list so we can notify you when we post new contents.

Django REST Framework: Authentication and Permissions

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post about ViewSet, ModelViewSet and Router, we saw how easily we can create REST APIs with the awesome Django REST Framework. In this blog post, we would see how we can secure our endpoints with user authentication and permissions. Authentication will help us identify which user is currently logged in and permissions will decide which user(s) can access which resources.

Authentication

The idea of authentication is pretty simple. When a new incoming request comes, we have to check the request and see if we can identify any user credentials along with it. If you have read the Flask HTTP Auth tutorial or the one about JWT, you might remember how we were checking the authorization header to authenticate our users. We might also receive the user login data via a POST request (form submission) or the user may already be logged in and we can identify using the session data.

We can see that the authentication mechanism can largely vary. Django REST Framework is very flexible in accommodating them. We can give DRF a list of classes, DRF will run the authenticate method on those classes. As soon as a class successfully authenticates the user, the return values from the call is set to request.user and request.auth. If none of the classes manage to authenticate the user, then the user is set to django.contrib.auth.models.AnonymousUser .

We can set these classes using the DEFAULT_AUTHENTICATION_CLASSES¬†settings under the DRF settings. Here’s an example:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

In the example above we used BasicAuthentication and SessionAuthentication Рtwo of the built in classes from Django REST Framework. We will look at how they work and we will also check how we can write our own class for our custom authentication.

(PS: Here we set the authentication policy globally, for all views / paths / resources – if we want, we can also use different authentication mechanism for each one, individually but that is usually not done in most cases).

Basic Authentication

In our example before, we mentioned the BasicAuthentication class. This class first checks the http authorization header (HTTP_AUTHORIZATION in request.META ). If the header contains appropriate string (something like Basic <Base64 Encoded Login>), it will decode the string, split the username, password and try to authenticate the user.

Basic Authentication is very simple, easy to setup and might be quite convenient for testing / debugging but I would highly discourage using this method on production.

Session Authentication

If you have used Django, you already know about session based authentication. In fact, Django itself handles the session based auth and sets the user as part of the request¬†object (an instance of HttpRequest¬†object. DRF just reads the user data from the request and checks for CSRF. That’s it.

Session Authentication works very well if your users are interacting with your API on the web, perhaps using ajax calls? In such cases, if the user is once logged in, his/her auth¬†is stored in the session and we can depend on those data while making requests from our web app. However, this will not work well if the client doesn’t or can not accept cookies (apps on different domains, mobile or desktop apps, other micro services etc).

Token Authentication

If you understand JWT, this one will feel similar, except in this case, the token will be just a “token”, no JSON or no signing. The user logs in and gets a token. On subsequent requests, this token must be passed as part of the authorization header.

To use token based auth, we first need to add the rest_framework.authtoken app to the INSTALLED_APPS list in your settings.py file. And then run the migration to create the related tables.

python manage.py migrate

We also need to add the TokenAuthentication class to our DRF auth class list:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}

Now let’s create a view to issue tokens to user.

from django.contrib.auth import authenticate
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.status import HTTP_401_UNAUTHORIZED
from rest_framework.authtoken.models import Token


@api_view(["POST"])
def login(request):
    username = request.data.get("username")
    password = request.data.get("password")

    user = authenticate(username=username, password=password)
    if not user:
        return Response({"error": "Login failed"}, status=HTTP_401_UNAUTHORIZED)

    token, _ = Token.objects.get_or_create(user=user)
    return Response({"token": token.key})

The code here should be self explanatory. We take username and password. We then try to authenticate the user using Django’s default authentication (checking username and password against what’s stored in the database). If the authentication fails, we return error message along with http status code 401.¬†If the authentication succeeds, we issue a token for the user and pass it in the response.

We need to add this view to our urlpatterns next:

url(r'^login', login)

Now let’s try it out:

$ curl --request POST \
  --url http://localhost:8000/api/login \
  --header 'content-type: application/json' \
  --data '{"username": "test_user", "password": "awesomepwd"}'

{"token":"5e2effff34c85c11a8720a597b96d73a4634c9ad"}%

So we’re getting the tokens successfully. Now to access a secured resource, we need to pass it as part of the authorization header. But how do we make a resource available only to a logged in user? Well, permissions come into play here.

Permissions

While authentication tells us which user is logged in (or not), it’s our responsibility to check if the current user (a valid logged in user or a guest, not logged in visitor) has access to the resource. Permissions¬†can help us deal with that. Just like authentication, we can also set a class of permissions globally or on each resource individually. Let’s start with the IsAuthenticated¬†permission first. Let’s add this to our SubscriberViewSet.

from rest_framework.permissions import IsAuthenticated

class SubscriberViewSet(ModelViewSet):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()
    permission_classes = (IsAuthenticated,)

If we try to access subscribers without any authentication, we will get an error message now:

{
  "detail": "Authentication credentials were not provided."
}

So let’s provide authentication using the token we got.

$ curl -H "Content-Type: application/json" -H "Authorization: Token 5e2effff34c85c11a8720a597b96d73a4634c9ad" http://localhost:8000/api/subscribers/

Now it works fine! There are many useful, already provided permission classes with Django REST Framework. You can find a list of them here http://www.django-rest-framework.org/api-guide/permissions/#api-reference.

Custom Authentication and Permissions

The authentication and permission classes which come with DRF are quite enough for many cases. But what if we needed to create our own? Let’s see how we can do that.

Writing a custom authentication class is very simple. You define your custom authenticate method which would receive the request object. You will have to return an instance of the default User model if authentication succeeds, otherwise raise an exception. You can also return an optional value for the auth object to be set on request. If our authentication method can not be used for this request, we should return None so other classes are tried.

Here’s an example from DRF docs:

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

In this example, the username is being retrieved from a custom header (X_USERNAME) and the rest is quite easy to understand.

Next, let’s see how we can create our custom permission class. For permissions, we can have two types of permissions – global permission or per object permission. Here’s an example of global permission from DRF docs:

from rest_framework import permissions

class BlacklistPermission(permissions.BasePermission):
    """
    Global permission check for blacklisted IPs.
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
        return not blacklisted

If the has_permission¬†method returns True¬†then the user has permission, otherwise not. Let’s see the example for per object permission:

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `owner`.
        return obj.owner == request.user

For dealing with per object permission, we can override the has_object_permission method. It can take the request, the view and the obj. We have to check if the current user can access the obj in question. Just like before, we need to return True or False to allow or deny the request.

In this blog post, we learned the basics of authentication and permissions. We now know how we can secure our API endpoints with DRF. While the token based authentication was very useful, we kind of like JWT. So in our next post, we will be using a third party package to implement JWT for Django REST Framework.

Django REST Framework: ViewSet, ModelViewSet and Router

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last blog post on ModelSerializer and Generic Views, we demonstrated how we can use the generic views along with ModelSerializer classes to rapidly develop our REST APIs. In this post, we will discuss about ViewSet and ModelViewset and see how they can speed up building APIs even further. At the same time we will take a look at the concepts of Routers which allow us to manage our api routes in a cleaner fashion.

Understanding ViewSet

So far we have learned how we can use the generic views. We can now use them to create the two kinds of resources – “collections” and “elements”. It works very well but we need to write at least two different classes to handle them properly. But if we think about it, both resources focus on the same entity, the same model – “Subscriber”. Wouldn’t it make more sense if we could have all the actions related to a Subscriber in a single class? Wouldn’t that be more convenient if we can put all the Subscriber related logic in a single place?

ViewSets come to the rescue. A ViewSet¬†is, as the name suggests, a class that provides the functionality of a set of views which are closely related. It’s one class but provides a set of views. We can handle both “collection” and “element” type of resources from the same class. And not just those, we can add even other¬†related actions.

Since a ViewSet handles both type of resources, we can no longer think in terms of the http verbs. Because both /api/subscriber and /api/subscriber/1 can respond to  GET requests and should produce different types of response. So we can no longer work with the get, post, put etc methods. We need to think more along the actions we can take on the entity (Subscriber) as a whole. A ViewSet works with these methods instead:

  • list¬†– list all elements, serves GET¬†to /api/subscriber
  • create¬†– create a new element, serves POST¬†to /api/subscriber
  • retrieve¬†– retrieves one element, serves GET¬†to /api/subscriber/1
  • update¬†and partial_update – updates single element, handles PUT/PATCH¬†to /api/subscriber/1
  • destroy¬†– deletes single element, handles DELETE¬†to /api/subscriber/1

Instead of our old get¬†and post¬†methods in separate classes, we can now define these methods in one single class and be done with it. But with general ViewSet, we have to provide logic / code for these views. That would¬†require more time and efforts. What if, we could generate these methods from our models / querysets? Well, we can! ūüėÄ

The ModelViewSet

The ModelViewSet¬†would only ask for the serializer class and the queryset. And then it will provide all the functionality of the different¬†ViewSet¬†methods. Let’s see the example:

from rest_framework.viewsets import ModelViewSet

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberViewSet(ModelViewSet):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()

Our SubscriberViewSet now extends the ModelViewSet and we provided the queryset and the serializer_class. Done. Really!

But there’s one slight problem, the ViewSet¬†and ModelViewSet¬†both handle at least two distincts url paths – /api/subscriber¬†(the collection path) and /api/subscriber/1¬†– the elements path. How do we tell Django’s URLConf to route requests to both urls to our single ViewSet? Well, we have to declare those paths ourselves. We can use the as_view¬†to both paths, defining which http verb should be routed to which methods:

SubscriberViewSet.as_view({'get': 'list', 'post':'create'}) # For: /api/subscriber
SubscriberViewSet.as_view({'get': 'retrieve', 'put':'update'}) # For: /api/subscriber/1

Or we can simply use a Router.

Using Routers

A Router¬†instance let’s us register¬†our ViewSet¬†to it and then we can just add the router.urls¬†to our urlpatterns. It will keep track of the view sets we register and the urls¬†property will generate all the url patterns required for those view sets to work. ¬†Let’s add our newly created SubscriberViewSet¬†to a router and see for ourselves. Open the api/urls.py¬†file and create the router there and register the viewset.

from rest_framework.routers import SimpleRouter

from .views import SubscriberViewSet

router = SimpleRouter()
router.register("subscribers", SubscriberViewSet)

urlpatterns = router.urls

That’s all that is required. Now try visiting – http://localhost:8000/api/subscribers or¬†http://localhost:8000/api/subscribers/1 – it all works.

Fantastic, so we just generated a full RESTful API from a model with¬†a surprisingly short amount of code! Isn’t that wonderful? Just don’t trust my words, go and look at your SubscriberViewSet¬†or the SubscriberSerializer¬†or the above defined router.

With the help of ModelSerializer, ModelViewSet and a router instance, we can build elegant CRUD APIs from our Django models insanely fast. At the same time, if we need, we can just override one of those methods (list, retrieve, create etc) to alter the default behavior with our own.

What’s Next?

We have crafted a nice, functional REST API. The next stop would be securing it. In our next post, we will be discussing Authentication and Permissions.

In the mean time, I would request you to subscribe to the mailing list so I can keep you posted about new exciting contents on this site. If you liked the post, please do share with your friends!

 

Django REST Framework: ModelSerializer and Generic Views

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post on Serializers, we learned how to use Serializers with APIViews. In this post we will discuss how ModelSerializer and the Generic views can take things even further.

Model + Serializer = ModelSerializer

That one line equation is probably enough to explain the concepts of ModelSerializers. In our example, we had to define similar fields on both Serializer and the Model class. We had to write codes for the same fields twice. That is 2x the efforts. ModelSerializers can help solve that problem. A ModelSerializer might remind us of ModelForm. The idea is the same. We extend ModelSerializer and pass it the model. The serializer inspects the model and knows what fields to use and what their types are.

Let’s refactor our old serializer to be a ModelSerializer:

from rest_framework import serializers

from .models import Subscriber


class SubscriberSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subscriber
        fields = "__all__"

That’s all we need – our new SubscriberSerializer¬†now infers the fields from the model (Subscriber) we passed to it. We can however choose which fields should be used while serializing / deserializing. In this example we pass the special value “__all__” which means we want to use all fields. But in many cases we would want to selectively use some fields. For example, for Django’s default¬†User¬†model, we don’t want to leak the password¬†data to public, so we will exclude the password¬†field on our User model serializer.

If we try out our API, we would notice everything is working just like before. Except our serializer class is now shorter and more concise. Awesome, right? Let’s move on to our next topic – generic views.

Generic Views

If you have decent amount of experience with Django, you probably have already come across and used the built in generic views. They provide useful functionality around common database operations. For example, we can use the generic list view and provide it with a queryset and the template, it will do the rest for us.

In the same way, we can pass a queryset and serializer to a ListAPIView¬†and it will create the list view for us. Same way we can use CreateAPIView¬†to implement the create view. What’s even better, since we’re using class based views, we can use both of them together, cool, no? Let’s refactor old code to use these two classes.

from rest_framework.generics import ListAPIView, CreateAPIView

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberView(ListAPIView, CreateAPIView):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()

Please note how we no longer need to provide the get and post methods but the API still works. The generic views just know how to deal with those.

There is actually a ListCreateAPIView which is the combination of these two as you can probably understand from the class name. We can just use that one.

from rest_framework.generics import ListCreateAPIView

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberView(ListCreateAPIView):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()

As you can see this generic view is quite helpful for generating “collection” resources (/api/subscriber) easily. There’s also RetrieveUpdateDestroyAPIView which we can use to generate “element” resources. As you can guess from the name, they provide get, put, patch¬†and delete¬†handler for single items (/api/subscriber/12).

Generic views are very handy for quickly generating resources from our models (querysets actually). This allows rapid API development.

What’s Next?

We have seen how we can very quickly create working REST APIs using model serializers and generic views.¬†Things will be even more amazing when we learn about ViewSets, specially ModelViewSets. You will have to wait for our next post for those ūüôā

In case you didn’t notice, we have a mailing list where you can subscribe to get latest updates. We don’t spam, only contents we post. Also if you enjoyed reading the post and found it informative, shouldn’t you share it with your friends? ūüėÄ

Django REST Framework: Serializers

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last blog post, Getting started with Django REST Framework, we saw how we could use the APIView and accept inputs from users using request.data. In our example, we dealt with string, so it was pretty straightforward. But consider the case of age or account_balance Рone has to be integer, the other has to be float / decimal. How do we properly validate the incoming data?

We can manually check every input field and send an error if the field type doesn’t match. But soon we’re going to have a problem at our hand – when the number of inputs will grow, we can’t just keep doing this kind of manual validation. In Django, we would probably use Django Forms for validation. Does DRF provide us with something similar? Yes, it does. The solution to our problem is Serializers.

What can a Serializer do for us?

Have you ever tried JSON serializing a Django model? Or a queryset? You can’t directly because they are not JSON serializable. So what do we do instead? We convert them to Python’s native data structures which could be serialized into JSON. We can serialize querysets into lists and model instances to dictionaries. But doing that by hand is cumbersome.

On the other hand, we saw how we can get incoming data from request.data¬†– we get this data¬†as key value pairs. We can’t just store them in database directly – we have to transform them into Django’s data structures like models and querysets. Doing that by hand is also cumbersome.

Serializers can help us with that. It can serialize complex types into Python natives types and then again deserialize native types into those complex types. Besides that, it also does basic validation based on the serializer field types. If a field is defined as an integer field, it will raise an error if we pass a string to that field. If we need more advanced validation rules, we can plug in the built in Validators¬†or even write our own. Let’s see code examples to understand the use case better.

Defining a Serializer

Create a file named serializers.py inside the api app directory. Put the following codes into it.

from rest_framework import serializers


class HelloWorldSerializer(serializers.Serializer):
    name = serializers.CharField(required=True, max_length=6)
    age = serializers.IntegerField(required=False, min_value=10, default=10)

We’re creating a HelloWorldSerializer¬†which extends serializers.Serializer. We’re defining two fields on this serializer –

  • name¬†is a CharField¬†so it accepts string. It has a max_length¬†of 6.
  • age¬†is an optional integer field. The value must be at least 10 if provided. If not provided, default value will be 10.

With this serializer setup, let’s modify our view to use it.

from .serializers import HelloWorldSerializer


class HelloWorldView(APIView):
    def get(self, request):
        return Response({"message": "Hello World!"})

    def post(self, request):
        serializer = HelloWorldSerializer(data=request.data)
        if serializer.is_valid():
            valid_data = serializer.data

            name = valid_data.get("name")
            age = valid_data.get("age")

            return Response({"message": "Hello {}, you're {} years old".format(name, age)})
        else:
            return Response({"errors": serializer.errors})

We pass the request.data as the data parameter to HelloWorldSerializer so it can read all the request data and parse them. Then we check if the serializer is valid. If you have used Django Forms, this will feel very similar. If the serializer is valid, that means we have a valid set of data available. So we can take the value of name and age and show a pretty message. On the other hand, if the serializer is not valid, we can pass the serializer.errors back to the client, which will contain elaborate error messages.

Let’s try out the API to see what happens. Let’s first send an empty request:

$ curl -H "Content-Type: application/json" -X POST http://localhost:8000/api/hello

{"errors":{"name":["This field is required."]}}

The errors say the name¬†field is required. Of course it is! Let’s pass the name.

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun"}' http://localhost:8000/api/hello

{"message":"Hello masnun, you're 10 years old"}

We just passed the name but didn’t pass the age. Since it is not required and has a default value set, we get the default value. But what if we set a low value?

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun", "age": '8'}' http://localhost:8000/api/hello

{"errors":{"age":["Ensure this value is greater than or equal to 10."]}}

So we passed 8¬†and it’s not happy about that. Please note we passed the 8 as a string but DRF doesn’t mind as long as it can convert it to an integer successfully. What if we pass a value that is no number?

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun", "age": "ten"}' http://localhost:8000/api/hello

{"errors":{"age":["A valid integer is required."]}}

That works too! Cool, okay then let’s give it a rest and pass a valid value.

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun", "age": 12}' http://localhost:8000/api/hello

{"message":"Hello masnun, you're 12 years old"}

 Serializer with Model

How does Serializers help us in working with models? To understand that, let’s first create one model.

Creating the Subscriber Model

Open api/models.py and add the Subscriber model like this:

class Subscriber(models.Model):
    name = models.CharField("Name", max_length=50)
    age = models.IntegerField("Age")
    email = models.EmailField("Email")

Now create and run the migration.

python manage.py makemigrations
python manage.py migrate

That should setup the table for our new model.

Update The Serializer

We added an email field to our model, also the max length for name is now 50 chars. Let’s update our serializer to match these constraints. Also rename it as SubscriberSerializer.

from rest_framework import serializers


class SubscriberSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=50)
    age = serializers.IntegerField()
    email = serializers.EmailField()
Update The View

And now let’s refactor our view.

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberView(APIView):
    def get(self, request):
        return Response({"message": "Hello World!"})

    def post(self, request):
        serializer = SubscriberSerializer(data=request.data)
        if serializer.is_valid():
            subscriber_instance = Subscriber.objects.create(**serializer.data)
            return Response({"message": "Created subscriber {}".format(subscriber_instance.id)})
        else:
            return Response({"errors": serializer.errors})

The code is very simple and straightforward. If the serializer validation succeeds, we create a new subscriber out of the validated data.

Update URLConf

Let’s update the urls.py¬†to update our url end point.

url(r'^subscriber', SubscriberView.as_view(), name="subscriber")

Now let’s try it out. We will post the following JSON using curl or postman:

{"name": "Abu Ashraf Masnun", "email": "[email protected]", "age": 29}

And we will get back the following response:

{
  "message": "Created subscriber 1"
}

With the serializer, we needed so much less codes. And we did it in a very clean way.

List All Subscribers

According to the REST Best Practices, the GET¬†call to a resource route (/api/subscriber) should return a list of all the items (subscribers). So let’s refactor the get¬†method to return the subscribers list.

    def get(self, request):
        all_subscribers = Subscriber.objects.all()
        serialized_subscribers = SubscriberSerializer(all_subscribers, many=True)
        return Response(serialized_subscribers.data)

We are fetching all subscribers and then passing the queryset to the serializer constructor. Since we’re passing a query set (not just a single model instance rather a list of model instances), we need to set ¬†many=True¬†. Also note,¬†we don’t need to call is_valid¬†– the data is coming from database, they’re already valid. In fact, we can’t call is_valid¬†unless we’re passing some value to the data¬†parameter (SubscriberSerializer(data=request.data)). When we pass queryset or model, the data is automatically available as serializer.data.

What’s Next?

We have so far learned how to use APIView¬†and Serializer¬†to build beautiful APIs with clean code. But we still have some duplication of efforts, for example when we had to define fields on both the model and the serializer. Also, we have to implement both collection and element resources. So that’s like 5 method implementation. Wouldn’t it be great if, by some mechanism we could¬†make things simpler, shorter and cleaner?

We’ll see, in our next blog post ūüôā Please subscribe to our mailing list for email updates. Also if you liked the content, please don’t forget to share with your friends!

 

Django REST Framework: Getting Started

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post about Building APIs in Django, we explained why using Django REST Framework would be a good idea. In this post, we will start writing our APIs using this awesome framework. DRF itself works on top of Django and provides many useful functionality that can help with rapid API development.

Installing Django REST Framework

We have to install DRF first. We can install it using pip as usual.

pip install djangorestframework

Once the installation succeeds, add rest_framework to the INSTALLED_APPS list in settings.py.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    

    'rest_framework',
    
    'api'
]

Now we are ready to start building our awesome APIs!

Using APIView

APIView¬†class is quite similar to Django’s View¬†class except it is more REST-y! The APIView¬†class can be considered as quite similar to the Flask Resource class from our Flask Tutorial. An APIView¬†has methods for the HTTP verbs. We can implement our own methods to handle those requests the way we want.

Let’s modify the function we wrote in our first post¬†to use APIView¬†instead.

from rest_framework.views import APIView
from rest_framework.response import Response


class HelloWorldView(APIView):
    def get(self, request):
        return Response({"message": "Hello World!"})

We have done two things –

  • Our¬†HelloWorldView¬†extends APIView¬†and overrides the get¬†method. So now DRF knows how to handle a GET¬†request to the API.
  • We return an instance of the Response¬†class. DRF will do the content negotiation for us and it will render the response in the correct format. We don’t have to worry about rendering JSON / XML any more.

Since we are now using a class based view, let’s update the urlconf and make the following change:

url(r'^hello', HelloWorldView.as_view(), name="hello_world")

That’s all the change that is necessary – we import the class based view and call the as_view¬†method on it to return a view that Django can deal with. Under the hood, the as_view¬†class method works as an entry point for the request. The class inspects the request and properly dispatches to the get, post, put¬†etc methods¬†to process the request. It then takes the result and sends back like a normal function based view would do. In short, the as_view¬†method kind of works as a bridge between the class based view and the function based views commonly used with Django.

The Web Browsable API

If you visit `http://localhost:8000/api/hello` you will now see a nice html view with our json response displayed along with other useful information (response headers). This html view is an excellent feature of DRF – it’s called the web browsable API. The APIs we create, DRF automagically generates a web view for us from where we can interact with our API, testing / debugging things. No need for swagger or any other external clients for testing. Awesome, no?

Function Based APIView

We can also use a function based form of APIView where we write a function and wrap it using the api_view decorator. An example would look like this:

from rest_framework.response import Response
from rest_framework.decorators import api_view


@api_view(["GET", "POST"])
def hello_world(request):
    if request.method == "GET":
        return Response({"message": "Hello World!"})

    else:
        name = request.data.get("name")
        if not name:
            return Response({"error": "No name passed"})
        return Response({"message": "Hello {}!".format(name)})

And in the urls.py, the entry will look like this:

url(r'^hello', hello_world, name="hello_world")

I mostly use function based views when things are really simple and I have to handle just one type of request (say, POST or GET). But in case I have to handle multiple type of requests, I will then have to check request.method to determine the type and handle accordingly. I find the class based view cleaner and well organized than writing a bunch of if else blocks.

You can read more about the function based APIView in the docs.

Accepting Input

We have seen how to write a simple end point to say “hello world!” – that is great. But now we must learn how we can handle inputs from our user. For this demonstration, on our /api/hello¬†endpoint, we would accept a name¬†in a POST request. If the name is passed, we will show a customized greeting. Let’s get to work!

class HelloWorldView(APIView):
    def get(self, request):
        return Response({"message": "Hello World!"})

    def post(self, request):
        name = request.data.get("name")
        if not name:
            return Response({"error": "No name passed"})
        return Response({"message": "Hello {}!".format(name)})

We have added a post method that should handle the POST requests. Instead of request.POST, we would use request.data which works across POST, PUT, PATCH  Рall other methods too. If the name is not passed we send error message. Otherwise we send a hello world message.

With that code written, let’s try it out –

$ curl -H "Content-Type: application/json" -X POST -d '{"name":"masnun"}' http://localhost:8000/api/hello

{"message":"Hello masnun!"}

Aha, things worked as expected! Cool! What if we don’t pass the name?

$ curl -H "Content-Type: application/json" -X POST  http://localhost:8000/api/hello

{"error":"No name passed"}

It works exactly like we wanted it to! Fantastic!

What’s next?

In this post, we saw how the APIView works and how we can accept inputs and send responses for different http verbs. In the next post, we will discuss about serializers and how they can be useful.

If you would like to get notified when we post new content, please subscribe to our mailing list. We will email you when there’s something new here ūüôā

Django: Building REST APIs

Recently, I have written a few pieces on REST API development. We have discussed the Fundamentals of REST APIs, Built a Simple REST API, Secured it with HTTP Basic Authentication and JSON Web Tokens. However, the example codes were all based on Flask. I chose Flask for those examples because¬†the framework was minimal in it’s core, light weight yet popular and with a matured eco system – very well¬†suited for¬†demonstrating simple REST APIs.¬†But if we consider popularity, probably no other framework is as popular as Django in the Python land. In my day to day work,¬†I use the framework quite extensively. Over the years, I have grown to become a passionate fan of Django. Naturally a tutorial series on building REST APIs with Django was just a matter of time.

Tutorial Index

This is the first part of the series. I shall update the index here as I keep adding new contents.

Code: https://github.com/masnun/djmailer

Prerequisites

The tutorial series will be heavily focused on building REST APIs. So there will be less scope of discussing the fundamentals of Django as we go. I would expect the reader to have decent familiarity of Django before hand. If you are new to Django, please spend some time with the Official Tutorial. It is very well written.

The tutorial series will use Python 3.6.0 (but any versions above Python 3.0 should work fine). As for Django, I have just installed the latest release as of today – ¬†1.11.¬†. We would also be using Django REST Framework, I have 3.6.3¬†right now. The example code repository will have an up-to-date requirements.txt¬†file – so don’t worry much about dependencies or their versions right now. Just make sure you use Python 3 and not Python 2.

Setting Up Django

Before we can start working on our awesome REST API, we first need to setup and configure our Django project. We need to install Django, install dependencies, start a project and an app, configure the database connection and write our first view. So what are we waiting for? Let’s get started!

Install Django and Dependencies

For now, we need the latest Django and a database driver. We will be using MySQL since it’s very popular and most people are familiar with it. In our production systems though, we mostly run PostgreSQL. We will discuss that choice in some later posts. Let’s install Django and the MySQL client using pip.

pip install django
pip install mysqlclient

Done? Cool. If Django installed successfully, you should be able to run the django-admin.py command. See if you can run it. If the command runs successfully, your installation worked. Time to move on to the next phase.

Create the Django Project

We want to build REST APIs for a mailing list. Let’s call it djmailer. We use the startproject¬†admin command to start our project.

django-admin startproject djmailer

We would now have a directory named djmailer¬†created in the current directory. Within that, there’s the default djmailer¬†app directory which contains the settings and root urlconf and the uwsgi app.

Create a New App

For the API related stuff, we would now create a new app named api. Let’s do that. First cd¬†into the project so you ¬†can access the manage.py¬†file. Now run this command:

python manage.py startapp api

Cool, now we have the api app created for us. Open up djmailer/settings.py and add api to the INSTALLED_APPS list.

Configure Database

In the default djmailer¬†app, there’s a file named settings.py. Open it up in your favorite text editor. We need to add our database credentials here. We will notice that a sample database configuration is already generated for us. Something like:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

The above example uses SQLite. But we want to use MySQL. So first create a database named djmailer on your MySQL server and create the database user and password. Then replace the above code with these lines:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djmailer',
        'USER': 'djmailer',
        'PASSWORD': 'suP3rS3CR3t!X$%^',
    }
}

Once you have configured the database, please run the migrate management command.

python manage.py migrate

This should create the tables Django needs for itself.

Hello World!

Time to create our first view. Open api/views.py file and create the view:

from django.http.response import JsonResponse


def hello_world(request):
    return JsonResponse({"message": "hello world!"})

Instead of the usual HttpResponse, we used JsonResponse because we want to render our response as JSON. Using this class ensures that the reponse is JSON and the appropriate content type is set.

We can add this view directly to the root urlconf in djmailer/urls.py but we would like to better organize our urls. So we are going to create a new file named urls.py in the api directory and put the following content:

from django.conf.urls import url

from .views import hello_world

urlpatterns = [
    url(r'^hello', hello_world, name="hello_world")
]

If we add urls from all apps to the root urlconf, it will soon become a mess. Also the apps will not be reusable. But if we keep our app specific routes inside the app, we can just import them from the root urlconf. This keeps things clean and the app can be easily plugged into a different project if needed.

Let’s edit djmailer/urls.py¬†(the root urlconf) and import our api urls.

from django.conf.urls import url, include
from django.contrib import admin

from api import urls as api_urls

urlpatterns = [
    url(r'^api/', include(api_urls, namespace="api")),
    url(r'^admin/', admin.site.urls),
]

Focus on line 4 and 7. We are importing our api urls and putting them under the /api/ path. Run the django dev server:

python manage.py runserver

If we visit `http://localhost:8000/api/hello`, we would see the response:

{"message": "hello world!"}

Cool? We just built our first RESTful view with Django.

Introducing Django REST Framework (DRF)

Building REST APIs with plain old Django is very possible but we have to do a lot. We need to read incoming requests, parse them as JSON or XML (through content negotiation), in case of CRUD operations,¬†we also have to work with models and finally¬†we have to send back the response in appropriate format. It’s all possible in Django – but like I said, I have to do so many things on our own. Our initial hello world view was very basic, very simple. As the project grows and we have more and more complexities, things will start to get out of hands.

In our Flask examples, using a third party extension helped us get started faster and saved us much time and efforts. Django REST Framework is one such framework for Django. It provides so much functionality out of the box РI am a big fan of the framework. So while we can do everything without this framework, using it would make us productive and keep things sane.

So from now on, we would be using DRF to continue building our APIs. In the next blog post, we would be getting started with Django REST Framework.

What’s Next?

On our next post –¬†Django REST Framework: Getting Started, we introduce you to the wonderful world of DRF and demonstrate how we can use APIView¬†to build our endpoints.

In the mean time, please subscribe to our mailing list. We will keep you posted when we publish new content. ūüôā

JWT Authentication with Python and Flask

In our blog post about HTTP Authentication, we promised we would next cover JSON Web Tokens aka JWT based authentication. So we wrote a detailed blog post on The Concepts of JWT explaining how the technology works behind the scene. And in this blog post, we would see how we can actually implement it in our REST API. In case you have missed them, we have also explained the basics of REST APIs  along with a Python / Flask tutorial walking through some of the best practices.

PyJWT or a Flask Extension?

In our last blog post on JWT, we saw code examples based on the PyJWT library. A quick Google search also revealed a couple of Flask specific libraries. What do we use?

We can implement the functionality with PyJWT alright. It will allow us fine grained control. We would be able to customize every aspect of how the authentication process works. On the other hand, if we use Flask extensions, we would need to do less since these libraries or extensions already provide some sort of integrations with Flask itself. Also personally, I tend to choose my framework specific libraries for a task. They reduce the amount of task required to get things going.

In this blog post, we would be using the Flask-JWT package.

Getting Started

Before we can begin, we have to install the package using pip.

pip install Flask-JWT

We also need an API end point that we want to secure. We can refer to the initial code we wrote for our HTTP Auth tutorial.

from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app, prefix="/api/v1")


class PrivateResource(Resource):
    def get(self):
        return {"meaning_of_life": 42}


api.add_resource(PrivateResource, '/private')

if __name__ == '__main__':
    app.run(debug=True)

Now we work on securing it ūüôā

Flask JWT Conventions

Flask JWT has the following convention:

  • There needs to be two functions – one for authenticating the user, this would be quite similar to the verify¬†function we wrote in our last tutorial (http auth tutorial). The second function’s job is to identify user from a token. Let’s call this function identity.
  • The authentication function must return an object instance that has an attribute named id.
  • To secure an endpoint, we use the @jwt_required¬†decorator.
  • An API endpoint is setup at /auth¬†that accepts username¬†and password¬†via JSON payload and returns access_token¬†which is the JSON Web Token we can use.
  • We must pass the token as part of the Authorization¬†header, like – JWT <token>.

Authentication and Identity

First let’s write the function that will authenticate the user. The function will take in username and password and return an object instance that has the id¬†attribute. In general, we would use database and the id would be user id. But for this example, we would just create an object with an ID of our choice.

USER_DATA = {
    "masnun": "abc123"
}


class User(object):
    def __init__(self, id):
        self.id = id

    def __str__(self):
        return "User(id='%s')" % self.id


def verify(username, password):
    if not (username and password):
        return False
    if USER_DATA.get(username) == password:
        return User(id=123)

We are storing the user details in a dictionary like before. We have created User class with id attribute so we can fulfil the requirement of having id attribute. In our verify function, we compare the username and password and if it matches, we return an User instance with the id being 123. We will use this function to verify user logins.

Next we need the identity function that will give us user details for a logged in user.

def identity(payload):
    user_id = payload['identity']
    return {"user_id": user_id}

The identity function will receive the decoded JWT. An example would be like:

{'exp': 1494589408, 'iat': 1494589108, 'nbf': 1494589108, 'identity': 123}

Note the identity¬†key in the dictionary. It’s the value we set¬†in the id¬†attribute of the object returned from the verify¬†function. We should load the user details based on this value. But since we are not using the database, we are just constructing a simple dictionary with the user id.

Securing Endpoint

Now that we have a function to authenticate and another function to identify the user, we can start integrating Flask JWT with our REST API. First the imports:

from flask_jwt import JWT, jwt_required

Then we construct the jwt instance:

jwt = JWT(app, verify, identity)

We pass the flask app instance, the authentication function and the identity function to the JWT class.

Then in the resource, we use the @jwt_required decorator to enforce authentication.

class PrivateResource(Resource):
    @jwt_required()
    def get(self):
        return {"meaning_of_life": 42}

Please note the jwt_required¬†decorator takes a parameter (realm) which has a default value of None. Since it takes the parameter, we must use the parentheses to call the function first – @jwt_required()¬†and not just @jwt_required. If this doesn’t make sense right away, don’t worry, please do some study on how decorators work in Python and it will come to you ūüôā

Here’s the full code:

from flask import Flask
from flask_restful import Resource, Api
from flask_jwt import JWT, jwt_required

app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-secret'

api = Api(app, prefix="/api/v1")

USER_DATA = {
    "masnun": "abc123"
}


class User(object):
    def __init__(self, id):
        self.id = id

    def __str__(self):
        return "User(id='%s')" % self.id


def verify(username, password):
    if not (username and password):
        return False
    if USER_DATA.get(username) == password:
        return User(id=123)


def identity(payload):
    user_id = payload['identity']
    return {"user_id": user_id}


jwt = JWT(app, verify, identity)


class PrivateResource(Resource):
    @jwt_required()
    def get(self):
        return {"meaning_of_life": 42}


api.add_resource(PrivateResource, '/private')

if __name__ == '__main__':
    app.run(debug=True)

Looks good? Let’s try it out.

Trying it out

Run the app and try to access the secured resource:

$ curl -X GET http://localhost:5000/api/v1/private
{
  "description": "Request does not contain an access token",
  "error": "Authorization Required",
  "status_code": 401
}

Makes sense. The endpoint now requires authorization token. But we don’t have one, yet!

Let’s get one – we must send a POST request to /auth¬†with a JSON payload containing username¬†and password. Please note, the api prefix is not used, that is the url for the auth end point is not /api/v1/auth. But it is just /auth.

$ curl -H "Content-Type: application/json" -X POST -d '{"username":"masnun","password":"abc123"}' http://localhost:5000/auth
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0OTQ1OTE4MjcsImlhdCI6MTQ5NDU5MTUyNywibmJmIjoxNDk0NTkxNTI3LCJpZGVudGl0eSI6MTIzfQ.q0p02opL0OxL7EGD7wiLbXbdfP8xQ7rXf7-3Iggqdi4"
}

Cool, we got the token. Now let’s use it to access the resource.

curl -X GET http://localhost:5000/api/v1/private -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0OTQ1OTE4MjcsImlhdCI6MTQ5NDU5MTUyNywibmJmIjoxNDk0NTkxNTI3LCJpZGVudGl0eSI6MTIzfQ.q0p02opL0OxL7EGD7wiLbXbdfP8xQ7rXf7-3Iggqdi4"
{
    "meaning_of_life": 42
}

Whoa, it worked! Amazing, now our JWT authentication is working great!

Getting the Authenticated User

Once our JWT authentication is functional, we can get the currently authenticated user by using the current_identity object.

Let’s add the import:

from flask_jwt import JWT, jwt_required, current_identity

And then let’s update our resource to return the logged in user identity.

class PrivateResource(Resource):
    @jwt_required()
    def get(self):
        return dict(current_identity)

The current_identity¬†object is a LocalProxy instance which can’t be directly JSON serialized. But if we pass it to a dict()¬†call, we can get a dictionary representation.

Now let’s try it out:

$ curl -X GET http://localhost:5000/api/v1/private -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0OTQ1OTE4MjcsImlhdCI6MTQ5NDU5MTUyNywibmJmIjoxNDk0NTkxNTI3LCJpZGVudGl0eSI6MTIzfQ.q0p02opL0OxL7EGD7wiLbXbdfP8xQ7rXf7-3Iggqdi4"
{
    "user_id": 123
}

As we can see the current_identity object returns the exact same data our identity function returns because Flask JWT uses that function to load the user identity.

What’s Next?

Go ahead and implement the same functionality using PyJWT and your own code. You will need to create an endpoint that encodes current user data and returns the access token. Then you will need to intercept the http headers, parse the Authorization header and verify the JWT token. It should be a fun and yet excellent learning exercise.