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.

 

Securing REST APIs: Basic HTTP Authentication with Python / Flask

In our last tutorial on REST API Best Practices, we designed and implemented a very simple RESTful mailing list API. However our API (and the data) was open to public, anyone could read / add / delete subscribers from our mailing list. In serious projects, we definitely do not want that to happen. In this post, we will discuss how we can use http basic auth to authenticate our users and secure our APIs.

PS: If you are new to REST APIs, please check out REST APIs: Concepts and Applications to understand the fundamentals.

Setup API and Private Resource

Before we can move on to authentication, we first need to create some resources which we want to secure. For demonstration purposes, we will keep things simple. We will have a very simple endpoint like below:

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)

If we launch the server and access the endpoint, we will get the expected output:

$ curl -X GET http://localhost:5000/api/v1/private
{
    "meaning_of_life": 42
}

Our API is for now public. Anyone can access it. Let’s secure it so it’s no longer publicly accessible.

Basic HTTP Authentication

The idea of Basic HTTP Authentication is pretty simple. When we request a resource, the server sends back a header that looks something like this: WWW-Authenticate ‚ÜíBasic realm=”Authentication Required”. Generally when we try to access such resources from a browser, the browser shows us a prompt to enter username and password. The browser then base64 encodes the data and sends back an Authorization header. The server parses the data and verifies the user. If the user is legit, the resource is accessible, otherwise we are not granted permission to access it.¬†

While using a REST Client, we would very often need to pass the credentials before hand, while we make the request. For example, if we’re using curl, we need to pass the --user¬†option while running the command.

Basic HTTP Authentication is a very old method but quite easy to setup. Flask HTTPAuth is a nice extension that would help us with that.

Install Dependencies

Before we can start writing codes, we need to have the necessary packages installed. We can install the package using pip:

pip install Flask-HTTPAuth

Once the package is installed, we can use it to add authentication to our API endpoints.

Require Login

We will import the HTTPBasicAuth¬†class and create a new instance named auth. It’s important to note that name because we will be using methods on this auth instance as decorators for various purposes. ¬†For example, we will use the¬†@auth.login_required¬†decorator to make sure only logged in users can access the resource.

In our resource, we added the above mentioned decorator to our get method. So if anyone wants to GET that resource, s/he needs to login first. The code looks like this:

from flask import Flask
from flask_restful import Resource, Api
from flask_httpauth import HTTPBasicAuth

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


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


api.add_resource(PrivateResource, '/private')

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

If we try to access the resource without logging in, we will get an error telling us we’re not authorized. Let’s send a quick request using curl.

$ curl -X GET http://localhost:5000/api/v1/private
Unauthorized Access

So it worked. Our API endpoint is now no longer public. We need to login before we can access it.¬†And from the API developer’s perspective, we need to let the users login before they can access our API. How do we do that?

Handling User Logins

We would generally store our users in a database. Well, a secured database. And of course, we would never store user password in plain text. But for this tutorial, we would store the user credentials in a dictionary. The password will be in plain text.

Flask HTTP Auth will handle the authentication process for us. We just need to tell it how to verify the user with his/her username and password. The @auth.verify_password¬†decorator can be used to register a function that will receive the username and password. This function will verify if the credentials are correct and based on it’s return value, HTTP Auth extension will handle the user auth.

In the following code snippet, we register the verify function as the callback for verifying user credentials.  When the user passes the credentials, this function will be called. If the function returns True, the user will be accepted as authorized. If it returns False, the user will be rejected. We have kept our data in the USER_DATA dictionary.

USER_DATA = {
    "admin": "SuperSecretPwd"
}


@auth.verify_password
def verify(username, password):
    if not (username and password):
        return False
    return USER_DATA.get(username) == password

Once we have added the above code, we can now test if the auth works.

$ curl -X GET http://localhost:5000/api/v1/private --user admin:SuperSecretPwd
{
    "meaning_of_life": 42
}

But if we omit the auth credentials, does it work?

$ curl -X GET http://localhost:5000/api/v1/private
Unauthorized Access

It doesn’t work without the login. Perfect! We now have a secured API endpoint that uses basic http auth. But in all seriousness, it’s not recommended. ¬†That’s right, do not use it in the public internet. It’s perhaps okay to use inside a private network. Why? Please read this thread.

Wrapping Up

With the changes made, here’s the full code for this tutorial:

from flask import Flask
from flask_restful import Resource, Api
from flask_httpauth import HTTPBasicAuth

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

USER_DATA = {
    "admin": "SuperSecretPwd"
}


@auth.verify_password
def verify(username, password):
    if not (username and password):
        return False
    return USER_DATA.get(username) == password


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


api.add_resource(PrivateResource, '/private')

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

As discussed in the last section, it’s not recommended to use basic http authentication in open / public systems. However, it is good to know how http basic auth works and it’s simplicity makes beginners grasp the concept of authentication / API security quite easily.

You might be wondering – “If we don’t use http auth, then what do we use instead to secure our REST APIs?”. In our next tutorial on REST APIs, we would demonstrating how we can use JSON Web Tokens aka JWT¬†to secure our APIs. Can’t wait for that long? Go ahead and read the introduction.

And don’t forget to subscribe to the mailing list so¬†when I write the next post, you get a notification!

REST API Best Practices: Python & Flask Tutorial

In our last post about REST APIs, we have learned the basics of how REST APIs function. In this post, we would see how we can develop our own REST APIs. We would use Python and Flask for that. If you are new to Python, we have you covered with our Python: Learning Resources and Guidelines post.

Python / Flask code is pretty simple and easy to read / understand. So if you just want to grasp the best practices of REST API design but lack Python skills, don’t worry, you will understand most of it. However, I would recommend you try out the codes hands on. Writing codes by hand is a very effective learning method. We learn more by doing than we learn by reading or watching.

Installing Flask and Flask-RESTful

We will be using the Flask framework along with Flask-RESTful. Flask-RESTful is an excellent package that makes building REST APIs with Flask both easy and pleasant. Before we can start building our app, we first need to install these packages.

pip install flask
pip install flask-restful

Once we have the necessary packages installed, we can start thinking about our API design.

RESTful Mailing List

You see, I just recently started this Polyglot.Ninja()¬†website and I am getting some readers¬†to my site. Some of my readers have shown very keen interest to receive regular updates from this blog. To keep them posted, I have been thinking about building a mailing list where people can subscribe with their email address. These addresses get stored in a database and then when I have new posts to share, I email them. Can we build this mailing list “service” as a REST API?

The way I imagine it – we will have a “subscribers” collection with many subscriber. Each subscriber will provide us with their full name and email address. We should be able to add new subscriber, update them, delete them, list them and get individual data. Sounds simple? Let’s do this!

Choosing a sensible URL

We have decided to build our awesome mailing list REST API.¬†For development and testing purposes, we will run the app on my local machine. So the base URL would be http://localhost:5000. This part will change when we deploy the API on a production server. So we probably don’t need to worry about it.

However, for API, the url path should make sense. It should clearly state it’s intent. A good choice¬†would be something like /api/¬†as the root url of the API. And then we can add the resources, so for subscribers, it can be /api/subscribers. Please note that it’s both acceptable to have the resource part singular (ie. /api/subscriber) or plural (/api/subscribers). However, most of the people I have talked to and the articles I have read, more people like the plural form.

API Versioning: Header vs URL

We need to think about the future of the API before hand. This is our first iteration. In the future, we might want to introduce newer changes. Some of those changes can be breaking changes. If people are still using some of the older features which you can’t break while pushing new changes, it’s time you thought about versioning your API. It is always best practice to version your API from the beginning.

The first version of the api can be called v1. Now there are two common method of versioning APIs Р1) Passing a header that specifies the desired version of the API  2) Put the version info directly in the URL. There are arguments and counter arguments for both approaches. However, versioning using url is easier and more often seen in common public APIs.

So we accommodate the version info in our url and we make it – /api/v1/subscribers. Like discussed in our previous REST article, we will have two types of resources here – “subscriber collection” (ie. /subscribers) and “individual subscriber” elements (ie. /subscribers/17). ¬†With the design decided upon and a bigger picture in our head, let’s get to writing some codes.

RESTful Hello World

Before we start writing our actual logic, let’s first get a hello world app running. This will make sure that we have got everything setup properly. If we head over to the Flask-RESTful Quickstart page, we can easily obtain a hello world code sample from there.

from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)


class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}


api.add_resource(HelloWorld, '/')

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

Let’s save this code in a file named main.py¬†and run it like this:

python main.py

If the code runs successfully, our app will launch a web server here –¬†http://127.0.0.1:5000/. Let’s break down the code a bit:

  • We import the necessary modules (Flask and Flask-RESTful stuff).
  • Then we create a new Flask app and then wrap it in Api.
  • Afterwards, we¬†declare our HelloWorld¬†resource which extends Resource.
  • On our resource, we define what the get¬†http verb will do.
  • Add the resource to our API.
  • Finally¬†run the app.

What happens here, when we write our Resources, Flask-RESTful generates the routes and the view handlers necessary to represent the resource over RESTful HTTP. Now let’s see, if we visit the url, do we get the message we set?

If we visit the url, we would see the expected response:

{
    "hello": "world"
}

Trying out REST APIs

While we develop our api, it is essential that we¬†can try out / test the API to make sure it’s working as expected. We need a way to call our api and inspect the output. If you’re a command line ninja, you would probably love to use curl. Try this on your terminal:

‚ěú curl -X GET http://localhost:5000
{
    "hello": "world"
}
‚ěú

This would send a GET request to the URL and curl would print out the response on the terminal. It is a very versatile tool and can do a lot of amazing things. If you would like to use curl on a regular basis, you may want to dive deeper into the options / features / use cases. These can help you:

However, if you like command line but want a friendlier and easier command line tool, definitely look at httpie.

httpie vs curl
httpie vs curl command

 

Now what if you’re not a CLI person? And we can agree that sometimes GUI can be much more productive to use. Don’t worry, Postman is a great app!

If you are developing and testing a REST API, Postman is a must have app!

postman interface
Our newly created API in Postman

Back to Business

We now have a basic skeleton ready and we know how to test our API. Let’s start writing our mailing list logic. Let’s first layout our resources with some sample data. For this example, we shall not bother about persisting the data to some database. We will store the data in memory. Let’s use a list as our subscriber data source for now.

from flask import Flask
from flask_restful import Resource, Api

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

users = [
    {"email": "[email protected]", "name": "Masnun", "id": 1}
]


class SubscriberCollection(Resource):
    def get(self):
        return {"msg": "All subscribers "}

    def post(self):
        return {"msg": "We will create new subscribers here"}


class Subscriber(Resource):
    def get(self, id):
        return {"msg": "Details about user id {}".format(id)}

    def put(self, id):
        return {"msg": "Update user id {}".format(id)}

    def delete(self, id):
        return {"msg": "Delete user id {}".format(id)}


api.add_resource(SubscriberCollection, '/subscribers')
api.add_resource(Subscriber, '/subscribers/<int:id>')

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

What changes are notable here?

  • Note we added a prefix¬†to the Api¬†for versioning reason. All our urls will be prefixed by /api/v1.
  • We created a list named users¬†to store the subscribers.
  • We created two resources – SubscriberCollection¬†and Subscriber.
  • Defined the relevant http method handlers. For now the response just describes the intended purpose of that method.
  • We add both resources to our api. Note how we added the id¬†parameter to the url. This id¬†is available to all the methods defined on Subscriber.

Fire up the local development server and try out the API. Works fine? Let’s move on!

Parsing Request Data

We have to accept, validate and process user data. In our cases, they would be the subscriber information. Each subscriber would have an email address, a full name and ID. If we used a database, this ID would have been auto generated. Since we are not using a database, we would accept this as part of the incoming request.

For processing request data, the RequestParser¬†can be very helpful.¬†We will use it in our POST¬†calls to /api/subscribers/ to validate incoming data and store the subscriber if the data is valid. Here’s the updated code so far:

from flask import Flask
from flask_restful import Resource, Api
from flask_restful.reqparse import RequestParser

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

users = [
    {"email": "[email protected]", "name": "Masnun", "id": 1}
]

subscriber_request_parser = RequestParser(bundle_errors=True)
subscriber_request_parser.add_argument("name", type=str, required=True, help="Name has to be valid string")
subscriber_request_parser.add_argument("email", required=True)
subscriber_request_parser.add_argument("id", type=int, required=True, help="Please enter valid integer as ID")


class SubscriberCollection(Resource):
    def get(self):
        return users

    def post(self):
        args = subscriber_request_parser.parse_args()
        users.append(args)
        return {"msg": "Subscriber added", "subscriber_data": args}


class Subscriber(Resource):
    def get(self, id):
        return {"msg": "Details about user id {}".format(id)}

    def put(self, id):
        return {"msg": "Update user id {}".format(id)}

    def delete(self, id):
        return {"msg": "Delete user id {}".format(id)}


api.add_resource(SubscriberCollection, '/subscribers')
api.add_resource(Subscriber, '/subscribers/<int:id>')

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

Here we have made two key changes:

  • We created a new instance of RequestParser¬†and added arguments¬†so it knows which fields to accept and how to validate those.
  • We added the request parsing code in the post¬†method. If the request is valid, it will return the validated data. If the data is not valid, we don’t have to worry about it, the error message will be sent to the user.

Testing the request parser

If we try to pass invalid data, we will get error messages. For example, if we request without any data, we will get something like this:

{
  "message": {
    "email": "Missing required parameter in the JSON body or the post body or the query string",
    "id": "Please enter valid integer as ID",
    "name": "Name has to be valid string"
  }
}

But if we pass valid data, everything works fine. Here’s an example of valid data:

{"email": "[email protected]", "name": "John Smith", "id": 3}

This will get us the following response:

{
  "msg": "Subscriber added",
  "subscriber_data": {
    "email": "[email protected]",
    "id": 3,
    "name": "John Smith"
  }
}

Cool, now we know how to validate user data ūüôā Please remember – never trust user input. Always validate and sanitize¬†user data to avoid security risks.

Next, we need to implement the user level updates.

Subscriber Views

We went ahead and completed the code for the rest of the methods. The updated code now looks like this:

from flask import Flask
from flask_restful import Resource, Api
from flask_restful.reqparse import RequestParser

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

users = [
    {"email": "[email protected]", "name": "Masnun", "id": 1}
]


def get_user_by_id(user_id):
    for x in users:
        if x.get("id") == int(user_id):
            return x


subscriber_request_parser = RequestParser(bundle_errors=True)
subscriber_request_parser.add_argument("name", type=str, required=True, help="Name has to be valid string")
subscriber_request_parser.add_argument("email", required=True)
subscriber_request_parser.add_argument("id", type=int, required=True, help="Please enter valid integer as ID")


class SubscriberCollection(Resource):
    def get(self):
        return users

    def post(self):
        args = subscriber_request_parser.parse_args()
        users.append(args)
        return {"msg": "Subscriber added", "subscriber_data": args}


class Subscriber(Resource):
    def get(self, id):
        user = get_user_by_id(id)
        if not user:
            return {"error": "User not found"}

        return user

    def put(self, id):
        args = subscriber_request_parser.parse_args()
        user = get_user_by_id(id)
        if user:
            users.remove(user)
            users.append(args)

        return args

    def delete(self, id):
        user = get_user_by_id(id)
        if user:
            users.remove(user)

        return {"message": "Deleted"}


api.add_resource(SubscriberCollection, '/subscribers')
api.add_resource(Subscriber, '/subscribers/<int:id>')

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

What did we do?

  • We added a helper function to find users from the list by it’s id
  • The update view works – we can update the user data. In our case we’re deleting the data and adding the new data. In real life, we would use UPDATE¬†on the database.
  • Delete method works fine!

Feel free to go ahead and test the endpoints!

HTTP Status Codes

Our mailing list is functional now. It works!¬†We have made good progress so far. But there’s something very important that we haven’t done yet. Our API doesn’t use proper http status codes. When we send response back to the client, we should also give it a status code. This code would help the client better interpret the results.

Have you ever visited a website and saw “404 Not found” error? Well, 404 is the status code, that means the document / resource you were looking for is not available. Saw any “500 Internal Server Error” lately? Now you know what that 500 means.

We can see the complete list of http status codes here: https://httpstatuses.com/.

Also depending on whether you’re a cat person or a dog enthusiast, these websites can explain things better:

So let’s fix our code and start sending appropriate codes. We can return an optional status code from our views. So when we add a new subscriber, we can send ¬†201 Created¬†like this:

return {"msg": "Subscriber added", "subscriber_data": args}, 201

And when we delete the user, we can send 204.

return None, 204

What’s next?

We have made decent progress today. We have designed and implemented a very basic API. We chose a sensible url, considered API versioning, did input validation and sent appropriate http status codes. We have done good. But what we have seen here is a very simple implementation. There are a lot of scope of improvements here. For example, our API is still open to public, there is no authentication enabled. So anyone with malicious intentions can flood / spam our mailing list database. We need to secure the API in that regard. We also don’t have a home page that uses HATEOAS to guide the clients. We don’t yet have documentation – always remember, the documentation is very important. We developers often don’t feel like writing documentation but well written documentation helps the consumers of your API consume it better and with ease. So do provide excellent docs!

I don’t know when – but in our next post on REST APIs, we shall explore more into the wonderful world of API development. And may be we shall also talk about some micro services? If you would like to know when I post those contents, do subscribe to the mailing list. You can find a subscription form on the sidebar.

And if you liked the post, do share with your friends ūüôā

Python: Learning Resources and Guidelines

The Python programming language is rapidly gaining popularity in all directions. Universities are teaching programming with Python. Jobs for the language is also on the rise. It has so many use cases Рweb development, data science, machine learning, command line tools, security research, desktop applications and what not.

Because of it’s massive popularity, mastery of the language has become¬†a highly valuable skill in the job market these days. Many large enterprises are starting to adopt the language. Small startups are also betting their money on it. Google, Spotify, Instagram, NASA, Twilio, Disqus, Dropbox – all the cool¬†brands are using it in their products / services.

The triumph in the academia is another major win for the programming language. This area has mostly been dominated by languages like C/C++/Java. Beginners have had the first taste of programming in those languages. However, I personally believe, Python is a much better choice in such cases. People who are just getting familiar with programming needs an easy and fun way to learn the basic concepts. C family or Java might require  much more efforts on their side. That is why the following chart makes me happy.

top us universities teaching python
Top US Universities are teaching more Python (Source)

Why is Python so popular?

That is an excellent question. What makes Python so special? Well, to begin with Python is a nice, easy to learn general purpose language. You can use it for many different purposes, it suits¬†many use cases. You will find necessary libraries, frameworks, toolkits for Python which will make your work easier. ¬†Python syntax is very clear and concise. It is ¬†very readable and easy to comprehend. When you read some source code, it all makes sense so easily. The only thing that might confuse you in the beginning is the forced use of whitespace for indentation. This might be a little difficult to grasp in the beginning but once you get it, you will start appreciating the value of properly indented code. Your code will look so much better and cleaner. Don’t you want your code to look beautiful?

5th place on the tiobe index
Python has been in top 10 on the TIOBE Index for a while

 

Apart from the code being very nice and easy to read, Python codes can be very often shorter than other languages. The language is very expressive so you can actually do more while writing less. While reading Python code, you may often feel that you’re actually reading English. The ease of learning and the powerful syntax makes Python very fun to learn and use. We all love fun, no?

Python is dynamically and strongly typed. This will make things easier to handle. Don’t worry if you do not understand the concepts of dynamic or strong typing. You will learn more about them as you start learning the language.

Friendly Community

Probably the best¬†thing about Python ¬†is it’s community. The community has crafted a huge amount of well maintained resources that you can learn from. From free to read online books to free video tutorials on youtube – the freely available resources have you covered. Need to up your Python game? There are excellent paid books and courses which will help you level up your programming skills. There are places like StackOverflow where fellow Pythonistas will answer any questions you have. You can also hang around IRC or Slack rooms for help. The community is very friendly and will warmly welcome you. The good people there will also guide on your journey and help you make right choices from time to time. Got a question? Just ask!

Learning Resources

These are the resources I recommend for learning the language:

  • Automate The Boring Stuff with Python: Excellent book. Really really well written. The author will guide you through a¬†series of interesting tasks which will also teach you various concepts of the language. It will be a very exciting journey for the beginners and an effective learning experience. This is the book I currently recommend to most beginners. You can read the book free online.¬†The author is also creating free youtube videos based on the content.
  • Python 101: Another excellent book. This one is free too. The book will cover the basics of the language. The content is very well written and beginner friendly.
  • Official Tutorial: The official Python documentation is very user friendly. It contains it’s own getting started kind of tutorial. And of course freely available.
  • Python 202: Another good book from Mike Driscoll, the author of “Python101” mentioned above. Once you have learned the basics, this book will help you dive into some more advanced concepts and language features.
  • Pro Python: This is another recommended book for levelling up your Python skills.
  • Fluent Python: I can’t recommend this book enough. When it comes to learning advanced Python, this book is a must read. It is worth every penny!

Practice Makes Us Perfect

Reading books or watching videos are not enough. We need to practise what we learn. There’s a common belief that you need to work at least 10 thousands hours before you can become expert in an area. The same idea applies here too. Read the books, watch the tutorials but at the same time, write codes by hand, every time! You will learn far more by trying those codes yourself and making mistakes. Mistakes are okay – we all make them when we begin. Learning from the mistakes is far more important.

While learning to code, these websites can be of great help. They will help you practise what you have learned so far. Some of them will also get you started with problem solving and challenge your brain. I recommend you pick a site and continue with it.

  • Hackerrank: They have all sorts of problems to challenge you. From data structures to machine learning algorithms, they have you covered. They have excellent support for Python. They even have a dedicated track for Python where you can further shun your skills.
  • Codewars: They have a very diversified set of programming problems to solve. The problems are classified into difficulty levels, so you can work your way through the various levels.
  • Codecombat: Want to learn coding while playing games? Codecombat would allow you to program your character with your language of choice. You can even fight other players.
  • Project Euler: Solve different mathematical problems on this platform.

There are many other online judges for practising algorithmic problem solving. You can try those too. But some of them might not be very Python friendly.

How to get help?

It is very likely that when you are learning Python for the first time, you will be stuck and seeking help. Please remember, Google is your best friend. Searching in Google will often find you the solutions. If it does not, feel free to join StackOverflow and ask there. You can also join this Slack Community to get help from the people there. They are very friendly.

What’s Next?

Once you have had a solid grasp of Python, you probably want to dive into a certain industry. if you want to learn web development, start learning Django or Flask. If you are into Machine Learning or Data Science, start learning the relevant tools / frameworks.

Finally, always remember – learning one language will never be enough. You have to become a Polyglot Ninja!