Restful Json Api for Mobile With Python and Django Using Behave

Mar 3, 2016 19:09 · 1621 words · 8 minute read Python Django

Mobile applications that can survive without a reliable back-end that stores and processes dates, are rare. Nowadays, mobile applications are complex enough to require a solid back-end framework. This particular topic is about how to build reliable RESTful json API with Python and Django, why it is so efficient and straightforward. This article talks about the following aspects of json API development.

What Makes API Good?

Reliable

Good API means that it is reliable. The API should work no matter what. Whatever the application queries are , the API has to gracefully respond with either an error message or actual data. It should always follow the protocol and there can be excuses made for unexpected behaviour. This is especially important for developers who work on the integration. Clear, straightforward and expectable output enables quick integration.

Scalable

Nowadays an application can get half a million visitors overnight. It is very important to be able to handle a massive load when needed. That doesn’t mean hundred of servers need to be deployed right from day one;it simply you must be able to deploy them as and when it is needed. Scalability in terms of API means to have an architecture that allows for growth without changing the protocol of communication.

Developer Friendly

After all, it is the developers who deal with all the important stuff. The API has to be supported with solid documentation, sandbox environment, clear usage instructions and testing methods.

Proper error messages in responses are much appreciated. It goes a long way in speeding up the integration process.

Quick to Bootstrap

It is an era of lean startups and all kinds of MVP-like products. Small investments, tight deadlines, market researchers and so on force the IT industry to offer cheap and quick solutions. APIs are not an exception. They have to be fast to bootstrap and easy to change when needed. A good API should be launched in days or, in weeks at the most.

Technology - Python and Django Solution

First, we use Python 3.5. The reason is simple - it is really fast to create applications with Python. We also use Django Rest Framework to achieve the best quality and increase development speed. The reason why we have chosen django is that it is extremely quick to build on it. It saves weeks or even months, thereby dramatically reducing the cost of development. The Python and Django communities are great. They produce lots of tools on a daily basis, that can be utilised. SDK for mobile apps. We created SDKs for iOS and Android to simplify the app developers work. Debug web interface for app developers. We also provide a nice-looking web interface for app developers so they can test it even without knowing too much about web development. The django-rest-framework provide it out-of-the-box which makes it easy to use.

Testing. Unit Test with Behave

RRegardless of whether you do TDD or just write tests afterwards, there have to be tests. For that purpose unittest Python module fits the best. However, unit tests will only allow you to check whether your functions, methods, classes and modules work fine. Technically, API couldn’t be considered as tested before you run acceptance tests over it. There is quite a nice tool called “behave” which allows you to test the exact behaviour of your system. This is a part of a bigger subject called “Behaviour Driven Development” which is not part of this article but is worth mentioning. In short, it gives you a way to abstract from the implementation and test the behaviour of the API. The syntax is very simple:

your_awesome_thing.feature

Feature: showing off behave
  
  Scenario: run a simple test
    Given we have behave installed
    When we implement a test
    Then behave will test it for us!

and the implementation

from behave import *

@given('we have behave installed')
def step_impl(context):
    pass

@when('we implement a test')
def step_impl(context):
    assert True is not False
@then('behave will test it for us!')
def step_impl(context):
    assert context.failed is False

As you can see, you can write your use cases first and implement test cases afterwards.

Logging with Sentry Online Logger

There has to be a way to identify the root of the problem. It is always a good idea to log everything what happens on a backend. We also provide nice web interface to view logs so app developers can use it to identify problems if any appears.

There has to be a way to identify the root of the problem. It is always a good idea to log everything that happens on the backend. We also provide a nice web interface to view logs, so app developers can use it to identify problems, if there are any.

sentry ui

There are certain things that are useful to log while creating json API:

Every API call with method, headers, query string and body

Log and attach tags to log entries to easily filter the URL that was called

Internal method calls and dump context during critical operations

Timing on the API calls. Could be important while analysing performance.

Background task execution with Celery and RabbitMQ

Often, there is a need to perform certain actions in the background. It is not only an API-related problem but in the context of mobile applications, it is quite important. Since the responsiveness of mobile applications is very important, every piece of the system should work to reduce processing times and delays. This is where background processing comes in.

For example the following tasks could be executed in the background:

Sending push notifications Sending emails 3rd-part API calls Payment processing Scaling images or any kind of image editing Heavy database operations such as re-indexing Creating bundles and files

from .models import User

@app.task
def create_user(username, password):
    User.objects.create(username=username, password=password)

For example the following tasks could be executed in the background:

  • Sending push notifications
  • Sending emails
  • 3rd-part API calls
  • Payment processing
  • Scaling images or any kind of image editing
  • Heavy database operations such as re-indexing
  • Creating bundles and files

There is much more of course. To make the mobile app truly fast and responsive it is important to pick up reliable tool. This is where Celery project (see official website) helps us.

Celery is a Python library that allows to manage workers and execute tasks in certain context. It requires broker - message queue - such as RabbitMQ or Redis. It works well in distributed cloud environments and fairly independent. Celery supports quite a lot of nice features such as delayed execution, chains, scheduled tasks etc. It also integrates well with Django and Django Rest Framework. Needless to say, there is much more to this. To make the mobile app truly fast and responsive, it is important to pick a reliable tool. This is where Celery project (see official website) helps us.

Celery is a Python library that allows us to manage workers and execute tasks within a certain context. It requires broker - message queue - such as RabbitMQ or Redis. It works well in distributed cloud environments and is fairly independent. Celery supports quite a lot of nice features such as delayed execution, chains, scheduled tasks etc. It also integrates well with Django and Django Rest Framework.

Documentation: Swagger Interactive

Documentation is a crucial part of the API. It is an interface to the interface. This is how everyone will know what your API is about. It’s importance is hard to underestimate and so, it should be performed carefully. One of the possibilities is to use Swagger web wrapper. It allows developers and all the other end users to play with the API interactively without writing any code at all. You can send GET/POST/DELETE requests via web interface and see how the API will react. It is a nice way to compare responses you get within an application to the etalon. The documentation is structured nicely and come along with code inline documentation. We have a standard way of organising our docs and the framework also provides tools to automatically generate necessary documentation.

alt text

Swagger.io - official website.

Sandbox with Docker

From the API consumer’s perspective it is very important to have the ability to “play” with the API. Obviously, there has to be testing environment where incorrect API calls won’t do any harm. It has to be easy to roll back changes and discover all the edge cases. This is where docker could be useful.

Docker is designed to be a revertible and sandboxed environment. What we do is we put API application (usually Django or Flask app) into a docker container. Often, we put Dockerfile inside the repository to allow developers to run their sandboxes locally. Aside from that, we run docker on a staging/testing server. This approach allows us to provide API consumers such as mobile developers and rich front-end d developers with sandboxed environment.

SWIFT integration example

To illustrate the interaction with json API in SWIFT consider the following code:

var err: NSError?
let body = [
    "user": "demo@example.com",
    "password": "password"
]
let request = NSMutableURLRequest(URL: NSURL(string: "http://demo.anvileight.com/login")!)
request.HTTPMethod = "POST"
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(body, options: nil, error: &err)

let session = NSURLSession.sharedSession()

let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
    let json:JSON = JSON(data: data)
    onCompletion(json, err)
})
task.resume()

This is a fairly simple example but covers the most frequent cases. It makes a call in a background thus doesn’t hold UI. This is quite complete code and could be adopted for particular cases.

Source code There is an example of API project I’ve created for another reason. You can find the source code on our git repository. This was made as an example of test driven development approach. There is no live version of that API yet but I will update this post once I create it.