.. currentmodule:: flask_restless .. _basicusage: 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 :class:`~flask.Flask` object, :class:`~flask.ext.sqlalchemy.SQLAlchemy` object, and model classes as usual but with one additional restriction: each model must have a primary key column named ``id`` of type :class:`sqlalchemy.Integer` or type :class:`sqlalchemy.Unicode`. .. sourcecode:: python from flask import Flask from flask_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 :class:`APIManager` object with the :class:`~flask.Flask` and :class:`~flask.ext.sqlalchemy.SQLAlchemy` objects:: from flask_restless import APIManager manager = APIManager(app, session=db.session) 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 :ref:`customizing`. Due to the design of Flask, these APIs must be created before your application handles any requests. The return value of :meth:`APIManager.create_api` is the blueprint in which the endpoints for the specified database model live. The blueprint has already been registered on the :class:`~flask.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 :meth:`~APIManager.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 ``/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 :class:`~sqlalchemy.Unicode` instead of an :class:`~sqlalchemy.Integer`, the instances will be accessible at URL endpoints like ``http://:/api/person/foo`` instead of ``http://:/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 :class:`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 :meth:`APIManager.init_app` method. If a :class:`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 :meth:`~APIManager.init_app` method with any :class:`~flask.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 :meth:`~APIManager.create_api` is invoked, and registers those blueprints each time :meth:`~APIManager.init_app` is invoked. (The name of each blueprint will be a :class:`uuid.UUID`.) .. versionchanged:: 1.0.0 The behavior of the :meth:`~APIManager.init_app` method was strange and incorrect before version 1.0.0. It is best not to use earlier versions.