loading...

A Flask Anti-Js Movement

michaelbukachi profile image Michael Bukachi ・3 min read

For the past couple of months I have been developing APIs using Flask. During this time, I noticed one thing in common. API consumers (at least the ones I've worked with) never fully go through the documentation provided to familiarize themselves with what is needed. Now, I'm not sure whether this is intentional or accidental (need feedback 😄).
The results of trial and error by developers consuming your API can be quite annoying especially if you have setup error-reporting services like sentry.

Imagine waking up to a tonne of emails from your error-reporting service only to find out that it's a result of bad input😥. Using Flask extensions such as Flask-Restplus can help you manage bad input easily. Also, Bad Requests exceptions can easily be ignored on error-reporting services so that they don't trigger alerts all the time. However, there are path variables which introduce a different kind of problem.
Take a look at the following snippet:

@app.route('/<string:user_id>')
def get_details(user_id):
.... # Do something with user_id

You have created an endpoint which fetches users details from a database somewhere and returns the necessary details. You have provided proper documentation about usage of that endpoint and you are quite satisfied with your work. You decide to go for an early lunch break only to come back quite early due to the fact that you started receiving error alerts as soon as you were about to have your lunch😩.

You quickly check the logs and stacktraces to see what is going on.
All the errors are coming from your newly created endpoint.

How?
How?

You are pretty sure you added all the necessary checks. You even added a type check to the path variable to prevent invalid types.

On looking closer at the stack traces, you notice that the user_id passed was a value of undefined. The developer consuming your API was probably in the dreaded trial and error phase, trying to wire things up on the frontend.

Now, a good API is supposed to be protected from misuse. And yes, this includes trial and error actions by devs consuming your API.

To deal with "undefined" variables you could probably add a conditional check and return an appropriate response.

@app.route('/<string:user_id>')
def get_details(user_id):
    if user_id == 'undefined':
        raise BadRequest('Invalid user id')
    # Do something with user_id
....

However, this solution does not scale well. What if you have hundreds of endpoints with path variables? What then?

A better solution would be to make use of Flask's before_request decorator like so:

@app.before_request
def check_undefined_values():
    for variable in request.view_args.values():
        if variable == 'undefined':
            raise BadRequest('\'undefined\' value found.')

This approach works best since the check_undefined_values function is always called before a request is forwarded to you endpoint.

I have created a Flask extension called Flask-AntiJs that uses the above. It has extra functionalities such as the ability to check for undefined values in request payloads and query parameters. It's actually my first Flask extension so check it out😁.

That's all for today.

Done

Discussion

pic
Editor guide