As Software Developers , we sometimes need to create a simple server that can accept some REST queries without wasting too much time. It’s always appealing to build our own components as it ensures our needs will be met. While REST sounds basic and easy to implement, to get a full REST server working is not trivial, especially when you want shiny features like HATEOAS ( https://en.wikipedia.org/wiki/HATEOAS ), pagination support, query filter, multiple content type (JSON, XML, plain text, …) support, If-Match/ETag, etc.
Let me walk you through a simple way of doing this using Python-Eve and Eve-SQLAlchemy.
Python-Eve
Eve is an open source Python REST API framework designed for human beings. It allows to effortlessly build and deploy highly customizable, fully featured RESTful Web Services.
Eve is built on top of Flask and gives the ability to easily extend an existing application to add REST routes. Those routes are directly associated to “resources” objects. Eve is historically associated with MongoDB. The resources objects were MongoDB collections but it is now possible to use SQLAlchemy or ElasticSearch instead.
The framework comes with an impressive list of features that are well covered in the project documentation.
The goal of this example if to show how to prepare a REST microservice based on Python-Eve and a SQL database. To do so, we will create a lightweight application to trace hateful and trollish comments from social media sites. The idea is to provide a central database be able to spot the same comment on different web site or help the moderator to block users. We will first prepare a REST back-end that we will consume later from a JavaScript Interface.
Requirements
We will use PostgreSQL for this example but you can use any engine supported by SQLAlchemy. Let’s install it:
sudo dnf install postgresql-server
Our DB structure
We will now start your application. For the moment it will be a sole wsgi.py file and we will use python3. We will see later in another article how we can split the application in different files.
Eve-SQLAlchemy expects your DB tables to have a least these fields:
- an unique ID
- a creation date,
- an update date and the Etag.
Those fields will be imported from a generic SQLAlchemy declarative_base class:
Base = declarative_base() class CommonColumns(Base): __abstract__ = True _created = Column(DateTime, default=func.now()) _updated = Column(DateTime, default=func.now(), onupdate=func.now()) _etag = Column(String) _id = Column(Integer, primary_key=True, autoincrement=True)
We can now create our real class and register it in Eve thanks to the @registerSchema decorator:
@registerSchema('submission') class Submission(CommonColumns): __tablename__ = 'submission' url = Column(String(2000)) author = Column(String(200)) author_url = Column(String(2000)) author_icon = Column(String(2000)) comment = Column(String(5000))
Eve configuration
The DB description is complete. We now need to configure Eve. Eve is configured through a Python dictionary that is shared for the whole application. You can also overload the configuration with a resource specific dictionary.
For the moment, we want to accept new record creation for anyone. We will see how to
turn authentication and permission later.
SETTINGS = { 'SQLALCHEMY_DATABASE_URI': ('postgresql:///' '?host=/tmp/pg_db&dbname=template1'), 'RESOURCE_METHODS': ['GET', 'POST'], 'ITEM_METHODS': ['GET'], 'DOMAIN': { 'submission': Submission._eve_schema['submission'], }, }
- SQLALCHEMY_DATABASE_URL is the address of our PostgreSQL instance. to keep thing simple, we use a local socket.
- RESOURCE_METHODS and ITEM_METHODS: since permission won’t be enabled yet, we should only accept new items creation which we cover with the
POST
request. We also acceptGET
for the moment in order to expose the content of the server DB, this way it will be easier to understand what’s going on. These configuration keys, like the others are covered by the Eve documentation.
Application creation
We will now initialize our Eve object and configure it to use the SQLAlchemy engine.
application = Eve(auth=None, settings=SETTINGS, data=SQL) # bind SQLAlchemy db = application.data.driver Base.metadata.bind = db.engine db.Model = Base db.create_all() if __name__ == "__main__": application.run(debug=True)
Data structure validation
As we said previously, Python-Eve was historically backed by MongoDB. MongoDB is a no-SQL database with no constraint. That’s why Eve allow the use of an extra configuration to validate the format of the resource. This is the purpose of the
validatoras an extra argument.
This
validatoris an object that will validate the dataset integrity from the application level using Cerberus. This topic is also covered by the validation page in the documentation.
You can pass the ValidorSQL object when you initialize the application object.
application = Eve(auth=None, settings=SETTINGS, validator=ValidatorSQL, data=SQL)
It’s still a good idea to use database level constraint to ensure the integrity of the data and as a bonus point, Eve-SQLAlchemy is able to understand some of them and use them to initialize ValidatorSQL default value.
boilerplate
Our application is mostly ready, we need to do the proper
importcalls.
from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import func from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy.ext.declarative import declarative_base # Eve imports from eve import Eve from eve_sqlalchemy import SQL from eve_sqlalchemy.validation import ValidatorSQL # Eve-SQLAlchemy imports from eve_sqlalchemy.decorators import registerSchema
Rock’n roll
For now, we use a user instance of PostgreSQL to run our application. This can easily be done with PostgreSQL with the following calls:
# Purge any existing DB directory $ rm -rf /tmp/pg_db/ # initialize the local PostgreSQL database store $ initdb --no-locale --nosync /tmp/pg_db # Disable the network support, we will only use the local socket $ echo "listen_addresses = ''" >> /tmp/pg_db/postgresql.conf # Finally start the postgre process in the foreground $ postgres -F -k /tmp/pg_db -D /tmp/pg_db
We can now run our server:
$ python wsgi.py
Our server is now up and running, we can access it on http://127.0.0.1:5000 and browse the nice home interface generated by Python-Eve.
Our first REST call
$ curl -H 'Content-Type: application/json' -vvv -X POST -d '{"url":"http://somewhere/article1","author":"Paul Emiste", "author_url":"http://somewhere-else","author_icon":"http://the_vilain/avatar-13.jpg","comment":"an hateful comment here."}' http://127.0.0.1:5000/submission
We can view this first post in the submission item list at http://127.0.0.1:5000/submission or directly at http://127.0.0.1:5000/submission/1. Here again, Python-Eve do all the work for us.
OpenShift
At this point, our RESTful service is ready to go live and accept public submission. Let’s use a PaaS service to publish it on Internet. Let’s pick one at random and use Openshift.
Let’s adjust the code
First, our little application depend on Eve-SQLAlchemy and Eve. requirements.txt is used by the Python component to declare the external dependencies, let’s just specify that we use it, this way the user will just have to run
pip install -r requirements.txtto deploy the application dependencies:
echo Eve-SQLAlchemy > requirements.txt
To be able to use OpenShift, we just need to point the URI to the PostgreSQL instance. OpenShift exports some environment variables to describe the location of the different services. We just have to reuse them.
pg_url = 'postgresql:///?host=/tmp/pg_db&dbname=template1' if 'OPENSHIFT_POSTGRESQL_DB_HOST' in os.environ: pg_url = 'postgresql://%s:%s/%s' % ( os.environ['OPENSHIFT_POSTGRESQL_DB_HOST'], os.environ['OPENSHIFT_POSTGRESQL_DB_PORT'], os.environ['OPENSHIFT_APP_NAME'])
We now need to use pg_url as the URI to the PostgreSQL database.
Enable the application in OpenShift
First, you’ll need to create an account on the OpenShift website, install the rhc
command and run rhc setup
.
In this example mydomain is your domain as returned by the rhc domain
list command.
$ git init $ editor wsgi.py $ git add wsgi.py $ git commit -m “my very first commit” $ rhc create-app bobby python-3.3 postgresql-9.2 (...) Your application 'bobby' is now available. URL: http://bobby-mydomain.rhcloud.com/ SSH to: 552643edfcf933d464000135@bobby-mydomain.rhcloud.com Git remote: ssh://blablabla@bobby-mydomain.rhcloud.com/~/git/bobby.git/ Cloned to: /home/goneri/bobby $ git push ssh://blablabla@bobby-mydomain.rhcloud.com/~/git/bobby.git/ master:master -f
Here bobby is the name of our application and mydomain, our domain name. Your website should be able on the http://bobby-mydomain.rhcloud.com URL. If it’s not the case, you can call
rhc tail bobbyto watch the application logs.
Here it is, you can now access your API directly through: http://bobby-mydomain.rhcloud.com
Full source code
The full source code of this example is available in this Git repository: https://github.com/goneri/trollhunter
Conclusion
This shows how, in a few simple steps, you can get a REST API server up and running. It is a simple iteration that allows us to easily implement more of Python-Eve’s large collection of features.