jfinkels / flask-restless

NO LONGER MAINTAINED - A Flask extension for creating simple ReSTful JSON APIs from SQLAlchemy models.
https://flask-restless.readthedocs.io
GNU Affero General Public License v3.0
1.02k stars 301 forks source link

Adding a separate blueprint that requests data from the same app not working #615

Closed christabor closed 7 years ago

christabor commented 7 years ago

When setting up an api, if you register a separate blueprint on the app, where one of that blueprints' routes makes standard requests to the resources (say, for a web UI on the same app), the endpoint itself will basically lag and time out, which requires a restart of the server (and the url never works).

This is probably not an optimal use of the tool, but I figured I'd mention it anyway. A simple workaround is to use two different apps, that can be run in the same source, which I've done successfully, but I wanted to bring it up in case there is some underlying issue with any db transactions.

An example:

app.py

"""My app."""

from flask import Flask
from flask_restless import APIManager

import models
from ui import ui as ui_bp

DB_URI = 'MYDBURI'

def create_app(db, uri):
    """Create app."""
    app = Flask('myapp')
    app.config.update(
        SECRET_KEY='foo',
        DEBUG=True,
        SQLALCHEMY_TRACK_MODIFICATIONS=False,
        SQLALCHEMY_DATABASE_URI=uri,
        SQLALCHEMY_ECHO=True,
    )
    db.init_app(app)
    app.register_blueprint(ui_bp)
    app.app_context().push()
    return app

app = create_app(models.db, DB_URI)
manager = APIManager(app, session=models.db.session)

api_options = dict(
    methods=['GET', 'POST', 'PATCH', 'DELETE'],
    results_per_page=100,
)
manager.create_api(models.Team, **api_options)
manager.create_api(models.User, **api_options)

if __name__ == '__main__':
    app.run(port=5000)

ui.py

"""UI front-end routes."""

import json

from flask import (
    Blueprint,
)
import requests

ui = Blueprint(
    'ui',
    __name__,
    template_folder='templates',
    static_folder='static',
    url_prefix='/ui'
)
json_headers = {'Content-type': 'application/json'}

@ui.route('/')
def ui_index():
    """Page."""
    data = dict()
    data = requests.get(
        'http://127.0.0.1:5000/api/user/1',
        headers=json_headers,
    ).json()
    kwargs = dict(data=data)
jfinkels commented 7 years ago

The package should support this use case, so I'm marking this as a bug. I'll take a look. Thanks for the report.

jfinkels commented 7 years ago

Hi @christabor! I can recreate this issue with the current development version of Flask-Restless (with some slight modifications to the posted code since the models.py module was not provided). However, my guess is that the issue is this: the process created by app.run() receives the request for /ui/, then issues its own request (via the call to requests.get()) to the /api/user/1 endpoint. However, the process blocks while waiting for a response from the GET /api/user/1 request, so it cannot serve a response. I don't think this is a problem inherent to Flask-Restless, but a general engineering problem: you can't have a server process block on a request to itself because the process will wait forever for itself to become available.

The solution is to have a separate process that only serves the API. I'm marking this is not a bug. Please let me know if my analysis is inaccurate or if you have any other suggestions!

christabor commented 7 years ago

Yep, make sense. I never intended to use it this way regardless because I would prefer to have the two services (front-end/back-end) run in entirely different processes (containers) but I thought I would mention it. Closing!