Search code examples
pythonflaskflask-restful

What is the correct way to handle queries for a Flask RESTful endpoint?


I am creating an API using Flask-RESTful, and am attempting to make a resource that returns all items in a database that are like a user defined string.

The endpoint for this resource is defined as:

api = Api(app)
api.add_resource(ItemsList, '/privateapi/item?query=<string:tag>')

And the resource itself is:

class ItemsList(Resource):

    def get(self, tag):
        search = f"%{tag}%"
        items = ItemModel.query.filter(ItemModel.item_name.like(search)).all()
        return {"items": [item.json() for item in items]}

The problem is that when I send a GET request to the endpoint http://127.0.0.1:5000/privateapi/item?query=appl I get a 404 response.

When I change the endpoint to api.add_resource(ItemsList, '/privateapi/item/<string:tag>') and then query the API I get the desired response.

However, I would prefer to use the query approach since I am not trying to GET a single record but want to return an array. I just think it makes more sense this way.

What could I try?


Solution

  • In flask, query parameters aren't used to match routes (only the path part of the URL is relevant). When you write:

    api.add_resource(ItemsList, '/privateapi/item?query=<string:tag>')
    

    You have created a route that will never match (well, not exactly; see below).

    You access query parameters in the request.args value, like this:

    from flask import Flask, request
    from flask_restful import Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    
    class ItemsList(Resource):
        def get(self):
            query = request.args.get("query")
            return f"Query expression was: {query}"
    
    
    api.add_resource(ItemsList, "/privateapi/item")
    
    if __name__ == "__main__":
        app.run(debug=True)
    

    With the above code, if I write:

    curl http://127.0.0.1:5000/privateapi/item?query=appl
    

    I get as the response:

    "Query expression was: appl"
    

    When I said "You have created a route that will never match" this was actually a bit of lie. In fact, you have created a route that requires a literal ? in the URL path, so if you were to make a request for this URL:

    curl http://127.0.0.1:5000/privateapi/item%3Fquery=appl
    

    It would work, but it's not what you want.