Build your own Python RESTful Web Service | by A Hung | Towards Data Science

In this blog, I will explain how I created a RESTful web service that intended to act as a data engineering service. The web service is served through the python web framework CherryPy inside a Docker container. Here are the step-by-step instructions on how to achieve that using only 3 files and less than 30 lines of code.
The first step is to setup the development environment by installing Docker, Python 3, and the following Python libraries:
pandas - for performing aggregation on a dataset
CherryPy - the web framework for serving the web service
These libraries can be installed using the pip command:
>> pip install pandas>> pip install CherryPy
Let’s start by writing a simple data processor myprocessor.py:
class MyProcessor:         def run(self, df):                return df.agg(['mean', 'min', 'max'])
This processor computes the mean, minimum and maximum values on all the columns of the input data frame.
In the same directory, we create a web service script ws.py that contains the definition of the request handler:
import cherrypyimport pandas as pdimport myprocessorp = myprocessor.MyProcessor()class MyWebService(object):   @cherrypy.expose   @cherrypy.tools.json_out()   @cherrypy.tools.json_in()   def process(self):      data = cherrypy.request.json      df = pd.DataFrame(data)      output = p.run(df)      return output.to_json()if __name__ == '__main__':      ...
This handler accepts requests directed to the /process endpoint and expects the input data to be represented as a JSON string in the body of the request. The result from the processing will be returned as a JSON string -conveniently converted using the panda to_json() method.
Next come the code for starting the web service in the main section of ws.py:
if __name__ == '__main__':   config = {'server.socket_host': '0.0.0.0'}   cherrypy.config.update(config)   cherrypy.quickstart(MyWebService())
The myprocessor.py and ws.py are all we need for the web service. We can now start the web service by using this python command:
>> python ws.py[24/Jul/2018:11:13:53] ENGINE Listening for SIGTERM.[24/Jul/2018:11:13:53] ENGINE Listening for SIGHUP.[24/Jul/2018:11:13:53] ENGINE Listening for SIGUSR1.[24/Jul/2018:11:13:53] ENGINE Bus STARTING[24/Jul/2018:11:13:53] ENGINE Started monitor thread 'Autoreloader'.[24/Jul/2018:11:13:53] ENGINE Started monitor thread '_TimeoutMonitor'.[24/Jul/2018:11:13:53] ENGINE Serving on http://0.0.0.0:8080[24/Jul/2018:11:13:53] ENGINE Bus STARTED
Ta da! Here is our web service running in no time.
In a Linux system, we can use curl to send this POST request to validate the REST API is working properly:
>> curl -d '{"num1" : [1, 2, 3], "num2":[4, 5, 6]}' -H "Content-Type: application/json" -X POST http://localhost:8080/process"{\"num1\":{\"mean\":2.0,\"min\":1.0,\"max\":3.0},\"num2\":{\"mean\":5.0,\"min\":4.0,\"max\":6.0}}"
The expected result is a dataset including the mean, minimum and maximum of 2 sets of numbers sent to the web .
Docker container
Next, we need to write a Dockerfile — this a text file for setting up the environment which the web service will be running in. If you have not used Docker before, I suggest to at least read the Docker Get Started documentation so you can be familiar with the Docker concepts. The first line in the file states to use python:3.6.4-slim-jessie as the base image:
from python:3.6.4-slim-jessie
This is one of the official Python Linux images with a vanilla version of Python 3 pre-installed and there are many other images to choose from the Docker repository, but this is a slim version that is sufficient for running a simple service. Just like how we have to install additional python packages for running the web service locally, the same needs to be done when building the docker image:
RUN pip install pandasRUN pip install CherryPy
Next, the build process will need to copy the web service files into the default working directory:
COPY myprocessor.py .COPY ws.py .
The container needs to expose port 8080 to permit access:
EXPOSE 8080
The final statement in the Dockerfile defines the command to run when the container starts:
ENTRYPOINT ["python", "ws.py"]
This is what the completed Dockerfile looks like and it should reside in the same directory as the python scripts written before:
from python:3.6.4-slim-jessieRUN pip install pandasRUN pip install CherryPyCOPY myprocessor.py .COPY ws.py .EXPOSE 8080ENTRYPOINT ["python", "ws.py"]
To execute the docker build command, we stay in the directory where the Dockerfile is created. It is good practice to assign docker image with a tag which can be referred to later, so we use the build command with the -t tag to build the image:
>> docker build -t python-ws .
We now have a docker image ready, let’s give this a try:
>> docker run -p 8080:8080 python-ws
This commands starts a container in attached mode, so the Cherrypy logging will appear immediately in the console. Again, the same curl command used for testing before should also work as the host machine (localhost) now forwards requests directed to port 8080 to the running docker container.
I have demonstrated how to build a simple RESTful web service but this service is far from production-ready. Extra security measures should be in place if the web service is hosted in the public cloud, e.g. HTTPS connections, some form of authentication. Example code for how to enable these security features is available in my GitHub project. The Cherrypy tutorials page provides many examples on how you can customise your web service as well.