Responses, status codes and JSON

Flask · Essentials · 10 min

APIs and pages both need the right status codes and headers, not just a body.

Flask converts whatever a view returns into a Response:

  • a string becomes an HTML response with status 200
  • a dict or list is serialized to JSON with Content-Type: application/json
  • a tuple adds metadata: (body, status), (body, headers) or (body, status, headers)
  • a Response object is sent as is

So return {"error": "not found"}, 404 is a complete JSON error response.

jsonify(...) builds a JSON response explicitly and is still handy for things returned from helpers. Flask's JSON provider handles datetime (as an HTTP date string), UUID, dataclasses and Decimal; other types need a custom provider or converting first.

When you need to modify the response object, for example to set a cookie, create it with make_response(...):

resp = make_response(render_template("home.html"))
resp.set_cookie("theme", "dark", httponly=True)

redirect(url) returns a 302 by default; pass code=303 after a POST or 301/308 for permanent moves. For files, send_file and send_from_directory stream a file with the right headers; the latter refuses paths that escape the directory, so use it for anything that comes from user input.

Use status codes deliberately: 201 Created for a new resource, 204 No Content for an empty success, 400 for bad input, 404 for missing, 409 for conflicts.

Example

from flask import Flask, make_response

app = Flask(__name__)
USERS = {1: "ann"}

@app.get("/users/<int:uid>")
def get_user(uid):
    if uid not in USERS:
        return {"error": "user not found"}, 404
    return {"id": uid, "name": USERS[uid]}

@app.get("/greet")
def greet():
    resp = make_response("hello")
    resp.headers["X-Hello"] = "world"
    resp.set_cookie("seen", "1", httponly=True)
    return resp

c = app.test_client()
r = c.get("/users/2"); print(r.status_code, r.json, r.content_type)
# 404 {'error': 'user not found'} application/json
r = c.get("/greet"); print(r.headers["X-Hello"], r.headers["Set-Cookie"])
# world seen=1; HttpOnly; Path=/

Remember

  • str -> HTML, dict/list -> JSON, tuple adds status and headers
  • make_response() when you need to set cookies or headers on the object
  • redirect() defaults to 302; choose codes deliberately

Watch out for

  • Returning 200 with an error message in the body instead of a proper 4xx status.

Check yourself

  1. What does return {"ok": False}, 400 produce?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/quickstart/#about-responses