Returning JSON responses with Flask

My most common use case for Flask is making APIs. And I like my APIs to respond with JSON. Flask comes with a jsonify() function that returns JSON as a Flask Response object.

The reason to use jsonify() over a regular json.dumps() is that jsonify() sets the Content-Type HTTP header to application/json. Whereas the output of json.dumps() is just plain text.

An example of responding with JSON is shown below. You can pass a dict or a list to the jsonify() function. This is then transformed to a JSON string.

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/chopstick/')
def chopstick():
    chopstick = {
        'color': 'bamboo',
        'left_handed': True
    }

    return jsonify(chopstick)


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

The above example returns the following JSON object if we call the /chopstick/ endpoint.

$ curl localhost:5000/chopstick/
{
  "color": "bamboo",
  "left_handed": true
}

Alternatively you can use keyword arguments as well with jsonify(). So the following produces the same end result.

@app.route('/chopstick/')
def chopstick():
    return jsonify(
        color='bamboo',
        left_handed=True
    )

Adding status codes to a JSON response

This jsonify() is nice and all, but it doesn’t give us the opportunity the set a status code for the response we are returning.

Lets say for example that we want the /chopstick/ endpoint to work with a chopstick id. In case the id isn’t found in the URI we want to return a 400 status for a bad request.

So to illustrate, let say the chopstick function is responsible for handling both the /chopstick/ and /chopstick/<id> routes. If the chopstick_id is None, so not present in the URI, we return a 400 status with a appropriate error message.

@app.route('/chopstick/')
@app.route('/chopstick/<int:chopstick_id>')
def chopstick(chopstick_id=None):
    if not chopstick_id:
        return # status 400 - bad request

    return jsonify(
        id=chopstick_id,
        color='bamboo',
        left_handed=True,
    )

To do so we can use the make_response() function in Flask. We still make use of jsonify() in combination with this make_response() so that the error message is a correct JSON string.

from flask import Flask, jsonify, make_response

app = Flask(__name__)


@app.route('/chopstick/')
@app.route('/chopstick/<int:chopstick_id>')
def chopstick(chopstick_id=None):
    if not chopstick_id:
        message = jsonify(message='"chopstick_id" missing')
        return make_response(message, 400)

    return jsonify(
        id=chopstick_id,
        color='bamboo',
        left_handed=True,
    )


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

If we now curl the endpoint without an ID we see the error message with the 400 status code as response.

$ curl -i localhost:5000/chopstick/
HTTP/1.0 400 BAD REQUEST
Content-Type: application/json
Content-Length: 44
Server: Werkzeug/1.0.1 Python/3.8.5
Date: Sun, 25 Oct 2020 09:04:22 GMT

{
  "message": "\"chopstick_id\" missing"
}

And ofcourse, the chopstick itself is returned with status 200 if we do specify an ID in the URI.

$ curl -i localhost:5000/chopstick/5
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 60
Server: Werkzeug/1.0.1 Python/3.8.5
Date: Sun, 25 Oct 2020 09:05:57 GMT

{
  "color": "bamboo",
  "id": 5,
  "left_handed": true
}

Flask Version

$ flask --version
Python 3.8.5
Flask 1.1.2
Werkzeug 1.0.1

See Also

6 Non-Programming Books for Programmers