oggit/packages/flask-restx/opengnsys-flask-restx-1.3.0/doc/scaling.rst

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