Creating API endpoints

To use this extension, you must have defined your database models using either SQLAlchemy or Flask-SQLALchemy. The basic setup in either case is nearly the same.

If you have defined your models with Flask-SQLAlchemy, first, create your Flask object, SQLAlchemy object, and model classes as usual but with one additional restriction: each model must have a primary key column named id of type sqlalchemy.Integer or type sqlalchemy.Unicode.

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)


class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)


class Article(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    author_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    author = db.relationship(Person, backref=db.backref('articles'))

db.create_all()

If you are using pure SQLAlchemy:

from flask import Flask
from sqlalchemy import Column, Integer, Unicode
from sqlalchemy import ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relationship
from sqlalchemy.orm import scoped_session, sessionmaker

app = Flask(__name__)
engine = create_engine('sqlite:////tmp/testdb.sqlite', convert_unicode=True)
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
mysession = scoped_session(Session)

Base = declarative_base()
Base.metadata.bind = engine


class Person(Base):
    id = Column(Integer, primary_key=True)


class Article(Base):
    id = Column(Integer, primary_key=True)
    author_id = Column(Integer, ForeignKey('person.id'))
    author = relationship(Person, backref=backref('articles'))

Base.metadata.create_all()

Second, instantiate an APIManager object with the Flask and SQLAlchemy objects:

from flask.ext.restless import APIManager

manager = APIManager(app, flask_sqlalchemy_db=db)

Or if you are using pure SQLAlchemy, specify the session you created above instead:

manager = APIManager(app, session=mysession)

Third, create the API endpoints that will be accessible to web clients:

person_blueprint = manager.create_api(Person, methods=['GET', 'POST'])
article_blueprint = manager.create_api(Article)

You can specify which HTTP methods are available for each API endpoint. In this example, the client can fetch and create people, but only fetch articles (the default if no methods are specified). There are many options for customizing the endpoints created at this step; for more information, see Customizing the ReSTful interface.

Due to the design of Flask, these APIs must be created before your application handles any requests. The return value of APIManager.create_api() is the blueprint in which the endpoints for the specified database model live. The blueprint has already been registered on the Flask application, so you do not need to register it yourself. It is provided so that you can examine its attributes, but if you don’t need it then just ignore it:

methods = ['GET', 'POST']
manager.create_api(Person, methods=methods)
manager.create_api(Article)

If you wish to create the blueprint for the API without registering it (for example, if you wish to register it manually later in your code), use the create_api_blueprint() method instead. You must provide an additional positional argument, name, to this method:

blueprint = manager.create_api_blueprint('person', Person, methods=methods)
# later...
someapp.register_blueprint(blueprint)

By default, the API for Person in the above code samples will be accessible at <base_url>/api/person, where the person part of the URL is the value of Person.__tablename__:

>>> import json
>>> # The python-requests library is installable from PyPI.
>>> import requests
>>> # Let's create a new person resource with the following fields.
>>> newperson = {'type': 'person', 'name': u'Lincoln', 'age': 23}
>>> # Our requests must have the appropriate JSON API headers.
>>> headers = {'Content-Type': 'application/vnd.api+json',
...            'Accept': 'application/vnd.api+json'}
>>> # Assume we have a Flask application running on localhost.
>>> r = requests.post('http://localhost/api/person',
...                   data=json.dumps(newperson), headers=headers)
>>> r.status_code
201
>>> document = json.loads(r.data)
>>> dumps(document, indent=2)
{
  "data": {
    "id": "1",
    "type": "person",
    "relationships": {
      "articles": {
        "data": [],
        "links": {
          "related": "http://localhost/api/person/1/articles",
          "self": "http://localhost/api/person/1/relationships/articles"
        }
      },
    },
    "links": {
      "self": "http://localhost/api/person/1"
    }
  }
  "meta": {},
  "jsonapi": {
    "version": "1.0"
  }
}
>>> newid = document['data']['id']
>>> r = requests.get('/api/person/{0}'.format(newid), headers=headers)
>>> r.status_code
200
>>> document = loads(r.data)
>>> dumps(document, indent=2)
{
  "data": {
    "id": "1",
    "type": "person",
    "relationships": {
      "articles": {
        "data": [],
        "links": {
          "related": "http://localhost/api/person/1/articles",
          "self": "http://localhost/api/person/1/relationships/articles"
        }
      },
    },
    "links": {
      "self": "http://localhost/api/person/1"
    }
  }
  "meta": {},
  "jsonapi": {
    "version": "1.0"
  }
}

If the primary key is a Unicode instead of an Integer, the instances will be accessible at URL endpoints like http://<host>:<port>/api/person/foo instead of http://<host>:<port>/api/person/1.

Deferred API registration

If you only wish to create APIs on a single Flask application and have access to the Flask application before you create the APIs, you can provide a Flask application as an argument to the constructor of the APIManager class, as described above. However, if you wish to create APIs on multiple Flask applications or if you do not have access to the Flask application at the time you create the APIs, you can use the APIManager.init_app() method.

If a APIManager object is created without a Flask application,

manager = APIManager(session=session)

then you can create your APIs without registering them on a particular Flask application:

manager.create_api(Person)
manager.create_api(Article)

Later, you can call the init_app() method with any Flask objects on which you would like the APIs to be available:

app1 = Flask('app1')
app2 = Flask('app2')
manager.init_app(app1)
manager.init_app(app2)

The manager creates and stores a blueprint each time create_api() is invoked, and registers those blueprints each time init_app() is invoked. (The name of each blueprint will be a uuid.UUID.)

Changed in version 1.0.0: The behavior of the init_app() method was strange and incorrect before version 1.0.0. It is best not to use earlier versions.