Tutorial: Writing a REST API with Tornado & Motor

Written by ramitmittal | Published 2019/06/30
Tech Story Tags: api | technology | web-development | software-development | python

TLDRvia the TL;DR App

Learn how to build your own JSON speaking REST API using the asynchronous Tornado networking library.

It wouldn’t be an overstatement to say that REST APIs power the web. Everything from social networks to the apps that help you order pizza is powered by REST APIs. In fact, mobile apps, enterprise applications, and even games need to talk to servers once in a (very short) while. There’s no point in limiting REST API tutorials to just web developers.

Tornado & Motor

Tornado is a mature, battle-tested web framework written in Python. Motor is an async driver for MongoDB that integrates really well with Tornado. These are going to be the two main tools we will be working as we create our superhero API.

Prerequisites and initial setup

Before we begin, please ensure that you have:

  • python>=3.6 installed on your machine.
  • The venv module along with pip installed.
  • mongodb up and running at localhost:27017.
  • A decent text editor or IDE.

On Ubuntu and similar systems, venv and pip can be easily installed using:

$ sudo apt install python3-pip python3-venv

Create a virtual environment for our new project.

$ mkdir heroes
$ cd heros
$ python3 -m venv env

Install the project dependencies.

$ source env/bin/activate
$ pip install tornado motor

Initial App Configuration

Create a folder named heroes inside the project folder (which is confusingly also named heroes but this is how things are!). This folder will contain our .py files.

  • In the app.py file, we create an instance of tornado.web.Application and make the tornado.httpserver.HTTPServer serve it.
  • The handlers.py file contains the request handlers that inherit from tornado.web.RequestHandler.
  • The HomeHandler implements the get method so that it can handle GET requests. Tornado automatically sends the response as JSON when a dictionary is passed as a parameter to the write method.
  • The ErrorHandler implements the prepare method which is called before the get or any other method. In this case, we send back an error message and finish the response.

<a href="https://medium.com/media/a0c952eb27c148b1efdb683e15a6774f/href">https://medium.com/media/a0c952eb27c148b1efdb683e15a6774f/href</a>

Firing up the Motor

Connecting the MongoDB driver to Tornado is simpler than most web frameworks.

  • Attach an instance of the MotorClient to the Application object.
  • Use the heroes database.
  • Refactor the handlers to inherit from a BaseHandler. This step is not required, but it will save us from a lot of duplicate code in the future.

Thank you Pycharm for these awesome Git Diffs!

If you prefer to copy, you can find the updated files here.

An API for Super Heroes

Add a /heroes endpoint that allows CRUD operations for the hero resource.

  • Add the /heroes routes to the Application instance in app.py.
  • Add the prepare method in the BaseHandler that automatically parses the request body as JSON for POST and PUT requests.
  • Add the HeroesHandler class with get, put, delete, and post methods.

Git Diff for app.py file

Two routes are added to the app. One for the /heroes endpoint and another for the /heroes/<hero_id> endpoint. While the former is the general endpoint for the resource, the latter is used for operations on a single item.

The changes in handlers.py are too many to show in a diff. Please check the complete source code listing below.

<a href="https://medium.com/media/1df1e23340cccf404cd22d1a8770b05b/href">https://medium.com/media/1df1e23340cccf404cd22d1a8770b05b/href</a>

  • The get method returns all the heroes for the /heroes route. For the /heroes/<hero_id> route, the details of a single hero are returned.
  • The get method also implements a basic form of paging.
  • The post method creates a new hero in the database. The name is required while the other fields are optional.
  • The put method is used to replace an existing hero with a different one.
  • Also, note the use of async/await syntax for asynchronous database operations.

Final Project Structure

A tree view of the heroes project folder.

heroes
├── heroes
│   ├── app.py
│   ├── handlers.py
│   └── __pycache__
└── env
    ├── bin
    ├── include
    ├── lib
    ├── lib64 -> lib
    └── pyvenv.cfg

7 directories, 3 files

Running the Application

  • Ensure that the mongod process is running at localhost:27017.

  • Open a terminal in the project root folder and run:

    $ source env/bin/activate $ export PORT=8080 $ export MONGO_CONNECT_URI=mongodb://localhost:27017 $ python3 -m heroes.app

The API server should now be up and running. Let’s hit the endpoints and check the results.

Consuming the REST API

httpie is a great library for consuming APIs from the command line. You can install it using:

$ pip install httpie

What next?

We have built a really cool REST API but it is far from being production-ready. Our API can be a lot more robust and user-friendly. If you want to learn more,

Thanks for reading, drop a few claps if you enjoyed it. Check out some other posts I’ve written, follow and reach out to me on Linkedin.

Ramit Mittal.


Published by HackerNoon on 2019/06/30