Chapter 9. Developing RESTful APIs with Tornado
In this chapter, we will work with Tornado to create a RESTful Web API and start working with this lightweight Web framework. We will cover the following topics:
Designing a RESTful API to interact with slow sensors and actuators
Understanding the tasks performed by each HTTP
method
Setting up a virtual environment with Tornado
Declaring status codes for the responses
Creating the classes that represent a drone
Writing request handlers
Mapping URL patterns to request handlers
Making HTTP requests to the Tornado API
Working with command-line tools - curl and HTTPie
Working with GUI tools - Postman and others
Designing a RESTful API to interact with slow sensors and actuators
Imagine that we have to create a RESTful API to control a drone, also known as an Unmanned Aerial Vehicle (UAV). The drone is an IoT device that interacts with many sensors and actuators, including digital electronic speed controllers linked to engines, propellers, and servomotors.
The IoT device has limited resources, and therefore, we have to use a lightweight Web framework. Our API doesn't need to interact with a database. We don't need a heavyweight Web framework like Django, and we want to be able to process many requests without blocking the Web server. We need the Web server to provide us with good scalability while consuming limited resources. Thus, our choice is to use Tornado, the open source version of FriendFeed's Web server.
The IoT device is capable of running Python 3.5, Tornado, and other Python packages. Tornado is a Python Web framework and an asynchronous networking library that provides excellent scalability...
Understanding the tasks performed by each HTTP method
Let's consider that http://localhost:8888/hexacopters/1
is the URL that identifies the hexacopter for our drone.
We have to compose and send an HTTP request with the following HTTP verb (PATCH
) and request URL (http://localhost:8888/hexacopters/1
) to set the hexacopter's motor speed in RPMs and its status. In addition, we have to provide the JSON key-value pairs with the necessary field name and the value to specify the desired speed. As a result of the request, the server will validate the provided values for the field, make sure that it is a valid speed and make the necessary calls to adjust the speed with an asynchronous execution. After the speed for the hexacopter is set, the server will return a 200 OK
status code and a JSON body with the recently updated hexacopter values serialized to JSON:
PATCH http://localhost:8888/hexacopters/1
We have to compose and send an HTTP request with the following HTTP verb (GET
) and request URL (http...
Setting up a virtual environment with Tornado
In Chapter 1, Developing RESTful APIs with Django, we learned that, throughout this book, we were going to work with the lightweight virtual environments introduced in Python 3.3 and improved in Python 3.4. Now, we will follow many steps create a new lightweight virtual environment to work with Tornado. It is highly recommended to read Chapter 1, Developing RESTful APIs with Django, in case you don't have experience with lightweight virtual environments in Python. The chapter includes all the detailed explanations about the effects of the steps we are going to follow.
First, we have to select the target folder or directory for our virtual environment. The following is the path we will use in the example for macOS and Linux. The target folder for the virtual environment will be the PythonREST/Tornado01
folder within our home directory. For example, if our home directory in macOS or Linux is /Users/gaston
, the virtual environment will be created...
Declaring status codes for the responses
Tornado allows us to generate responses with any status code that is included in the http.HTTPStatus
dictionary. We might use this dictionary to return easy to understand descriptions as the status codes, such as HTTPStatus.OK
and HTTPStatus.NOT_FOUND
after importing the HTTPStatus
dictionary from the http
module. These names are easy to understand but they don't include the status code number in their description.
We have been working with many different frameworks and micro-frameworks throughout the book, and therefore, we will borrow the code that declares very useful functions and variables related to HTTP status codes from the status.py
file included in Django REST Framework, that is, the framework we have been using in the first chapters. The main advantage of using these variables for the HTTP status codes is that their names include both the number and the description. When we read the code, we will understand the status code number and their...
Creating the classes that represent a drone
We will create as many classes as we will use to represent the different components of a drone. In a real-life example, these classes will interact with a library that interacts with sensors and actuators. In order to keep our example simple, we will make calls to time.sleep
to simulate interactions that take some time to set or get values to and from sensors and actuators.
First, we will create a Hexacopter
class that we will use to represent the hexacopter and a HexacopterStatus
class that we will use to store status data for the hexacopter. Create a new drone.py
file. The following lines shows all the necessary imports for the classes that we will create and the code that declares the Hexacopter
and HexacopterStatus
classes in the drone.py
file. The code file for the sample is included in the restful_python_chapter_09_01
folder:
from random import randint
from time import sleep
class HexacopterStatus:
def __init__(self, motor_speed...
The main building blocks for a RESTful API in tornado are subclasses of the tornado.web.RequestHandler
class, that is, the base class for HTTP request handlers in Tornado. We just need to create a subclass of this class and declare the methods for each supported HTTP verb. We have to override the methods to handle HTTP requests. Then, we have to map the URL patterns to each subclass of tornado.web.RequestHandler
in the tornado.web.Application
instance that represents the Tornado Web application.
First, we will create a HexacopterHandler
class that we will use to handle requests for the hexacopter resource. Create a new api.py
file. The following lines show all the necessary imports for the classes that we will create and the code that declares the HexacopterHandler
class in the drone.py
file. Enter the next lines in the new api.py
file. The code file for the sample is included in the restful_python_chapter_09_01
folder:
import status
from datetime import date
from...
Mapping URL patterns to request handlers
We must map URL patterns to our previously coded subclasses of tornado.web.RequestHandler
. The following lines create the main entry point for the application, initialize it with the URL patterns for the API, and starts listening for requests. Open the previously created api.py
file and add the following lines. The code file for the sample is included in the restful_python_chapter_09_01
folder:
application = web.Application([
(r"/hexacopters/([0-9]+)", HexacopterHandler),
(r"/leds/([0-9]+)", LedHandler),
(r"/altimeters/([0-9]+)", AltimeterHandler),
], debug=True)
if __name__ == "__main__":
port = 8888
print("Listening at port {0}".format(port))
application.listen(port)
ioloop.IOLoop.instance().start()
The preceding code creates an instance of tornado.web.Application
named application
with the collection of request handlers that make up the Web application. The code passes a list of tuples to the Application...
Making HTTP requests to the Tornado API
Now, we can run the api.py
script that launches Tornados's development server to compose and send HTTP requests to our unsecure and simple Web API. Execute the following command:
python api.py
The following lines show the output after we execute the previous command. The Tornado HTTP development server is listening at port 8888
:
Listening at port 8888
With the previous command, we will start the Tornado HTTP server and it will listen on every interface on port 8888
. Thus, if we want to make HTTP requests to our API from other computers or devices connected to our LAN, we don't need any additional configurations.
Tip
If you decide to compose and send HTTP requests from other computers or devices connected to the LAN, remember that you have to use the development computer's assigned IP address instead of localhost
. For example, if the computer's assigned IPv4 IP address is 192.168.1.103
, instead of localhost:8888
, you should use 192.168.1.103:8888
. Of...
In this chapter, we designed a RESTful API to interact with slow sensors and actuators. We defined the requirements for our API, understood the tasks performed by each HTTP method, and set up a virtual environment with Tornado.
We created the classes that represent a drone and wrote code to simulate slow I/O operations that are called for each HTTP request method, wrote classes that represent request handlers and process the different HTTP requests, and configured the URL patterns to route URLs to request handlers and their methods.
Finally, we started Tornado development server, used command-line tools to compose and send HTTP requests to our RESTful API, and analyzed how each HTTP requests was processed in our code. We also worked with GUI tools to compose and send HTTP requests.
Now that we understand the basics of Tornado to create RESTful APIs, we will take advantage of the non-blocking features combined with asynchronous operations in Tornado in a new version for the API, which...