Building an API Part #1

This blog post is a part of “the things I learned while working on popcorn”, Popcorn tracks usage of linux packages. Ionuț Arțăriși is my mentor for this GSoC project for openSUSE.

PART #1: Views return JSON in addition to HTML


JSON = Javascript Object Notation, it’s a data interchange format. People used to use XML for data interchange, but JSON is even simpler than XML and it maps more directly onto the data structures used in modern programming languages

It is built on two structures:

Returning JSON and HTML:

1. The render decorator:

Instead of cluttering up the views, Ionuț advised me to write a decorator that renders HTML or JSON depending on the request’s header accept mimetypes. To do that, views needed to return a dictionary.

The decorator would act on the dictionary and depending on the mimetype, render the template, (say a browser sent a request) or jsonify the dictionary and returns it (someone is trying to use the API).

2. SQLalchemy objects are not JSON serializable.

You can’t just do flask.jsonify(system=System.query.all()) (System is a model). Searching a little, I realized I had to write a serialize function for the model. But there was a problem.

The system model looks like this:

34     sys_hwuuid = Column(String(32), primary_key=True)
35     distro_name = Column(String(20), nullable=False)
36     distro_version = Column(String(10), nullable=False)
37     arch = Column(String(10), ForeignKey('arches.arch'), nullable=False)
39     submissions = relationship('Submission', backref='system',
40                                order_by=Submission.sub_date.desc())

System model has a relationship with Submission, which in turn has a relationship with Submission_Packages and so on. So, if we are serializing System, to what depth do we go?

There are a few suggestions Ionuț gave me:

One answer is to provide links to external resources. Then the user would get the static data of the System resource and a list of links for each of the Submissions. The user can then choose if he wants to know more about those Submissions by issuing more requests.

Also check out for a format standard built on top of json, which adds hypermedia. There are other proposed standards, too.

I’ll include the discussion on Hypermedia, HAL specification and more in part #2. For now, we decided to make two methods, one that returns only the object’s attributes, accessible without relationships and the other, which returns the attributes + the first layer of relationship’s attributes. So, the serialize function would go one level.

3. Python datetime objects are not handled by flask.jsonify:

Initially I wrote a function to dump the datetime and take care if its none, Ionuț pointed out, we don’t need time and the date can’t be None as it is the primary key.

So, a simple value.strftime(“%Y-%m-%d”) worked.

4. Modifying views to apply decorators:

Removed the render_template function by dicts of parameters, and handling them by a render decorator written in 1.

5. Something that is untested is broken.

Pull request for the above changes: