Prototyping an API

Brett Vitaz
3 min readFeb 21, 2017

--

This is a continuation of my Pokémon Go registry project. So far, I have used a web scraping library called Beautiful Soup to collect as much data as I needed for the first iteration, and I have created the initial database schema and loaded the data. At this point I have a database that’s ready for action, but how can I get access to the data? I previously chose Python to write the API service: Flask to serve, SQLAlchemy for database access, and Marshmallow as the (de)serialization library. Now I’m ready to create a prototype. I will detail a simplified version of that process below.

Flask, SQLAlchemy, and Marshmallow are all reasonably easy to use on their own, but extensions have been written that really take that to the next level. To get started, I pip installed the libraries into a virtual environment in my project directory.

$ virtualenv venv
$ source ./venv/bin/activate
$ pip install flask \
sqlalchemy \
marshmallow \
flask-sqlalchemy \
flask-marshmallow \
marshmallow-sqlalchemy \
simplejson

It is helpful to freeze the dependency versions so that library changes don’t cause the app to behave in unexpected ways in the future.

$ pip freeze > requirements.txt

Now I am ready to start coding. Flask-SQLAlchemy adds some major convenience by allowing us to just specify the database URI, and it will do the heavy lifting to configure SQLAlchemy and attach to the Flask context.

from flask import Flask
from flask_sqlalchemy import SQLALchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///pokedex.sqlite'
db = SQLAlchemy(app)

When I create the data model, Flask-SQLAlchemy’s db object contains references to all of the classes that would normally have to imported.

class Pokemon(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
description = db.Column(db.Text)
stamina = db.Column(db.Integer)
attack = db.Column(db.Integer)
defense = db.Column(db.Integer)
cp_max = db.Column(db.Integer)
category = db.Column(db.String)
types = db.relationship('Type', secondary='pokemon_type')class Type(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
description = db.Column(db.Text)
class PokemonType(db.Model):
pokemon_id = db.Column(db.Integer, db.ForeignKey(Pokemon.id), primary_key=True)
type_id = db.Column(db.Integer, db.ForeignKey(Type.id), primary_key=True)

Flask-SQLAlchemy provides a convenience method to create the database and tables (if you haven’t already done so).

db.create_all()

Flask-Marshmallow allows Marshmallow to use the Flask app for configuration and context. Marshmallow-SQLAlchemy allows Marshmallow to use SQLAlchemy models as metadata for its schema, and will also cause a Marshmallow schema to output SQLAlchemy model objects.

from flask_marshmallow import Marshmallow
...
ma = Marshmallow(app) # must come after `db = ...`
...
class PokemonSchema(ma.ModelSchema):
class Meta:
model = Pokemon

I’ll add the route that will return everything in the Pokémon table, which showcases another Flask-SQLAlchemy convenience, the query method on the model object.

@app.route('/api/pokemon')
def route_pokemon():
pokemon_schema = PokemonSchema(many=True)
all_pokemon = Pokemon.query.all()
return pokemon_schema.jsonify(all_pokemon)
if __name__ == '__main__':
app.run(debug=True)

When I run this and observe the output (usually localhost:5000/api/pokemon), I notice that the type shows up as a list of numbers. I can make this more informative by adding Type as a nested schema to the Pokemon schema. Be careful when adding self-referential nested schemas, you’ll need to remember to use the exclude argument.

class PokemonSchema(ma.ModelSchema):
types = ma.Nested('TypeSchema', many=True, exclude=('pokemon',)
class Meta:
model = Pokemon
class TypeSchema(ma.ModelSchema):
class Meta:
model = Type

I’ll add another route to return an individual Pokémon by id. Notice that the query method returns an object that can return a single item or a 404 page if the query returned nothing.

@app.route('/api/pokemon/<int:pokemon_id>')
def route_pokemon_id(pokemon_id):
pokemon_schema = PokemonSchema()
pokemon = Pokemon.query.get_or_404(pokemon_id)
return pokemon_schema.jsonify(pokemon)

With that in place, I have created a very simple API, and with the help of some library extensions, I have done it very quickly. You can see the final version of this in my GitHub repo: brettvitaz/lightning-talk-easy-api. Where do we go from here? We can clean up the code by extracting the data model and schema creation and Flask blueprints, add routes for authentication and users, and even write unit tests. For more advanced ideas, I highly recommend Miguel Grinberg’s PyCon talk “Is Your REST API RESTful?”.

I am in the process of writing a post about using Beautiful Soup to scrape the web for data, and about designing the database schema for the project. Please check back for more!

Previous: Creating a Pokémon Go registry from scratch

--

--