Question

I'm creating an API with Flask that is being used for a mobile platform, but I also want the application itself to digest the API in order to render web content. I'm wondering what the best way is to access API resource methods inside of Flask? For instance if I have the following class added as a resource:

class FooAPI(Resource):
    def __init__(self):
        # Do some things
        super(FooAPI, self).__init__()
    def post(self, id):
        #return something
    def get(self):
        #return something

api = Api(app)
api.add_resource(FooAPI, '/api/foo', endpoint = 'foo')

Then in a controller I want:

@app.route("/bar")
def bar():
   #Get return value from post() in FooAPI

How do I get the return value of post() from FooAPI? Can I do it somehow through the api variable? Or do I have to create an instance of FooAPI in the controller? It seems like there has to be an easy way to do this that I'm just not understanding...

Was it helpful?

Solution

The obvious way for your application to consume the API is to invoke it like any other client. The fact that the application would be acting as a server and a client at the same time does not matter, the client portion can place requests into localhost and the server part will get them in the same way it gets external requests. To generate HTTP requests you can use requests, or urllib2 from the standard library.

But while the above method will work just fine it seems overkill to me. In my opinion a better approach is to expose the common functionality of your application in a way that both the regular application and the API can invoke. For example, you could have a package called FooLib that implements all the shared logic, then FooAPI becomes a thin wrapper around FooLib, and both FooAPI and FooApp call FooLib to get things done.

OTHER TIPS

Another approach is to have both the app and API in the same Flask(-RESTful) instance. Then, you can have the app call the API methods/functions internally (without HTTP). Let's consider a simple app that manages files on a server:

# API. Returns filename/filesize-pairs of all files in 'path'  
@app.route('/api/files/',methods=['GET'])
def get_files():
    files=[{'name':x,'size':sys.getsizeof(os.path.join(path,x))} for x in os.listdir(path)]
    return jsonify(files)

# app. Gets all files from the API, uses the API data to render a template for the user 
@app.route('/app/files/',methods=['GET'])
def app_get_files():
    response=get_files() # you may verify the status code here before continuing  
    return render_template('files.html',files=response.get_json())

You can push all your requests around (from the API to the app and back) without including them in your function calls since Flask's request object is global. For example, for an app resource that handles a file upload, you can simply call:

@app.route('/app/files/post',methods=['POST'])
def app_post_file():
   response=post_file()
   flash('Your file was uploaded succesfully') # if status_code==200
   return render_template('home.html')

The associated API resource being:

@app.route('/api/files/',methods=['POST'])
def post_file():
   file=request.files['file']
   ....
   ....
   return jsonify({'some info about the file upload'})

For large volumes of application data, though, the overhead of wrapping/unwrapping JSON makes Miguel's second solution preferrable.

In your case, you would want to call this in your contoller:

response=FooAPI().post(id)

I managed to achieve this, sometimes API's get ugly, in my case, I need to recursively call the function as the application has a extremely recursive nature (a tree). Recursive functions itself are quite expensive, recursive HTTP requests would be a world of memory and cpu waste.

So here's the snippet, check the third for loop:

class IntentAPI(Resource):
    def get(self, id):
        patterns = [pattern.dict() for pattern in Pattern.query.filter(Pattern.intent_id == id)]
        responses = [response.dict() for response in Response.query.filter(Response.intent_id == id)]
        return jsonify ( { 'patterns' : patterns, 'responses' : responses } )

    def delete(self, id):
        for pattern in Pattern.query.filter(Pattern.intent_id == id):
            db.session.delete(pattern)

        for response in Response.query.filter(Response.intent_id == id):
            db.session.delete(response)

        for intent in Intent.query.filter(Intent.context == Intent.query.get(id).set_context):
            self.delete(intent.id) #or IntentAPI.delete(self, intent.id)

        db.session.delete(Intent.query.get(id))
        db.session.commit()
        return jsonify( { 'result': True } )
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top