276 lines
7.1 KiB
ReStructuredText
276 lines
7.1 KiB
ReStructuredText
.. _scaling:
|
|
|
|
Scaling your project
|
|
====================
|
|
|
|
.. currentmodule:: flask_restx
|
|
|
|
This page covers building a slightly more complex Flask-RESTX app that will
|
|
cover out some best practices when setting up a real-world Flask-RESTX-based API.
|
|
The :ref:`quickstart` section is great for getting started with your first Flask-RESTX app,
|
|
so if you're new to Flask-RESTX you'd be better off checking that out first.
|
|
|
|
|
|
Multiple namespaces
|
|
-------------------
|
|
|
|
There are many different ways to organize your Flask-RESTX app,
|
|
but here we'll describe one that scales pretty well with larger apps
|
|
and maintains a nice level of organization.
|
|
|
|
Flask-RESTX provides a way to use almost the same pattern as Flask's `blueprint`.
|
|
The main idea is to split your app into reusable namespaces.
|
|
|
|
Here's an example directory structure::
|
|
|
|
project/
|
|
├── app.py
|
|
├── core
|
|
│ ├── __init__.py
|
|
│ ├── utils.py
|
|
│ └── ...
|
|
└── apis
|
|
├── __init__.py
|
|
├── namespace1.py
|
|
├── namespace2.py
|
|
├── ...
|
|
└── namespaceX.py
|
|
|
|
|
|
The `app` module will serve as a main application entry point following one of the classic
|
|
Flask patterns (See :doc:`flask:patterns/packages` and :doc:`flask:patterns/appfactories`).
|
|
|
|
The `core` module is an example, it contains the business logic.
|
|
In fact, you call it whatever you want, and there can be many packages.
|
|
|
|
The `apis` package will be your main API entry point that you need to import and register on the application,
|
|
whereas the namespaces modules are reusable namespaces designed like you would do with Flask's Blueprint.
|
|
|
|
A namespace module contains models and resources declarations.
|
|
For example:
|
|
|
|
.. code-block:: Python
|
|
|
|
from flask_restx import Namespace, Resource, fields
|
|
|
|
api = Namespace('cats', description='Cats related operations')
|
|
|
|
cat = api.model('Cat', {
|
|
'id': fields.String(required=True, description='The cat identifier'),
|
|
'name': fields.String(required=True, description='The cat name'),
|
|
})
|
|
|
|
CATS = [
|
|
{'id': 'felix', 'name': 'Felix'},
|
|
]
|
|
|
|
@api.route('/')
|
|
class CatList(Resource):
|
|
@api.doc('list_cats')
|
|
@api.marshal_list_with(cat)
|
|
def get(self):
|
|
'''List all cats'''
|
|
return CATS
|
|
|
|
@api.route('/<id>')
|
|
@api.param('id', 'The cat identifier')
|
|
@api.response(404, 'Cat not found')
|
|
class Cat(Resource):
|
|
@api.doc('get_cat')
|
|
@api.marshal_with(cat)
|
|
def get(self, id):
|
|
'''Fetch a cat given its identifier'''
|
|
for cat in CATS:
|
|
if cat['id'] == id:
|
|
return cat
|
|
api.abort(404)
|
|
|
|
|
|
The `apis.__init__` module should aggregate them:
|
|
|
|
.. code-block:: Python
|
|
|
|
from flask_restx import Api
|
|
|
|
from .namespace1 import api as ns1
|
|
from .namespace2 import api as ns2
|
|
# ...
|
|
from .namespaceX import api as nsX
|
|
|
|
api = Api(
|
|
title='My Title',
|
|
version='1.0',
|
|
description='A description',
|
|
# All API metadatas
|
|
)
|
|
|
|
api.add_namespace(ns1)
|
|
api.add_namespace(ns2)
|
|
# ...
|
|
api.add_namespace(nsX)
|
|
|
|
|
|
You can define custom url-prefixes for namespaces during registering them in your API.
|
|
You don't have to bind url-prefix while declaration of Namespace object.
|
|
|
|
.. code-block:: Python
|
|
|
|
from flask_restx import Api
|
|
|
|
from .namespace1 import api as ns1
|
|
from .namespace2 import api as ns2
|
|
# ...
|
|
from .namespaceX import api as nsX
|
|
|
|
api = Api(
|
|
title='My Title',
|
|
version='1.0',
|
|
description='A description',
|
|
# All API metadatas
|
|
)
|
|
|
|
api.add_namespace(ns1, path='/prefix/of/ns1')
|
|
api.add_namespace(ns2, path='/prefix/of/ns2')
|
|
# ...
|
|
api.add_namespace(nsX, path='/prefix/of/nsX')
|
|
|
|
|
|
Using this pattern, you simply have to register your API in `app.py` like that:
|
|
|
|
.. code-block:: Python
|
|
|
|
from flask import Flask
|
|
from apis import api
|
|
|
|
app = Flask(__name__)
|
|
api.init_app(app)
|
|
|
|
app.run(debug=True)
|
|
|
|
|
|
Use With Blueprints
|
|
-------------------
|
|
|
|
See :doc:`flask:blueprints` in the Flask documentation for what blueprints are and why you should use them.
|
|
Here's an example of how to link an :class:`Api` up to a :class:`~flask.Blueprint`. Nested Blueprints are
|
|
not supported.
|
|
|
|
.. code-block:: python
|
|
|
|
from flask import Blueprint
|
|
from flask_restx import Api
|
|
|
|
blueprint = Blueprint('api', __name__)
|
|
api = Api(blueprint)
|
|
# ...
|
|
|
|
Using a `blueprint` will allow you to mount your API on any url prefix and/or subdomain
|
|
in you application:
|
|
|
|
|
|
.. code-block:: Python
|
|
|
|
from flask import Flask
|
|
from apis import blueprint as api
|
|
|
|
app = Flask(__name__)
|
|
app.register_blueprint(api, url_prefix='/api/1')
|
|
app.run(debug=True)
|
|
|
|
.. note ::
|
|
|
|
Calling :meth:`Api.init_app` is not required here because registering the
|
|
blueprint with the app takes care of setting up the routing for the application.
|
|
|
|
.. note::
|
|
|
|
When using blueprints, remember to use the blueprint name with :func:`~flask.url_for`:
|
|
|
|
.. code-block:: python
|
|
|
|
# without blueprint
|
|
url_for('my_api_endpoint')
|
|
|
|
# with blueprint
|
|
url_for('api.my_api_endpoint')
|
|
|
|
|
|
Multiple APIs with reusable namespaces
|
|
--------------------------------------
|
|
|
|
Sometimes you need to maintain multiple versions of an API.
|
|
If you built your API using namespaces composition,
|
|
it's quite simple to scale it to multiple APIs.
|
|
|
|
Given the previous layout, we can migrate it to the following directory structure::
|
|
|
|
project/
|
|
├── app.py
|
|
├── apiv1.py
|
|
├── apiv2.py
|
|
└── apis
|
|
├── __init__.py
|
|
├── namespace1.py
|
|
├── namespace2.py
|
|
├── ...
|
|
└── namespaceX.py
|
|
|
|
Each `apis/namespaceX` module will have the following pattern:
|
|
|
|
.. code-block:: python
|
|
|
|
from flask_restx import Namespace, Resource
|
|
|
|
api = Namespace('mynamespace', 'Namespace Description' )
|
|
|
|
@api.route("/")
|
|
class Myclass(Resource):
|
|
def get(self):
|
|
return {}
|
|
|
|
Each `apivX` module will have the following pattern:
|
|
|
|
.. code-block:: python
|
|
|
|
from flask import Blueprint
|
|
from flask_restx import Api
|
|
|
|
api = Api(blueprint)
|
|
|
|
from .apis.namespace1 import api as ns1
|
|
from .apis.namespace2 import api as ns2
|
|
# ...
|
|
from .apis.namespaceX import api as nsX
|
|
|
|
blueprint = Blueprint('api', __name__, url_prefix='/api/1')
|
|
api = Api(blueprint
|
|
title='My Title',
|
|
version='1.0',
|
|
description='A description',
|
|
# All API metadatas
|
|
)
|
|
|
|
api.add_namespace(ns1)
|
|
api.add_namespace(ns2)
|
|
# ...
|
|
api.add_namespace(nsX)
|
|
|
|
And the app will simply mount them:
|
|
|
|
.. code-block:: Python
|
|
|
|
from flask import Flask
|
|
from api1 import blueprint as api1
|
|
from apiX import blueprint as apiX
|
|
|
|
app = Flask(__name__)
|
|
app.register_blueprint(api1)
|
|
app.register_blueprint(apiX)
|
|
app.run(debug=True)
|
|
|
|
|
|
These are only proposals and you can do whatever suits your needs.
|
|
Look at the `github repository examples folder`_ for more complete examples.
|
|
|
|
.. _github repository examples folder: https://github.com/python-restx/flask-restx/tree/master/examples
|