Mastering Flask Responses: A Guide to Using jsonify and make_response for API Development
Tags: python, webdev, beginners, programming
Published: 2024-03-26 14:41:52

During my coding journey, I've come to a place where I'm having one issue when going about completing projects, which I will try to clarify through this beginner's blog. Do I use jsonify? Or do I use make_response?


![Neo Chooses Blue or Red Pill](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjuxeuoz8g1ja2qt05g7.gif)

To be honest, initially, when completing assignments and trying to make API requests, I began with jsonify to render data from my models, this was until I discovered make_response and my mind entirely changed...kind of. 

It seemed like some of the code I was having problems trying to complete using jsonify was being passed relatively easily when I began using make_response instead. But I was blindly plugging it in, without necessarily understanding, why it was working better than when I used jsonify.

So let's clarify what each does and their use cases, starting with jsonify.

jsonify is a function provided by Flask, designed specifically for converting data into JSON format. Consider the following JSON representation of a character, which might be similar to what you'd want to send back to a client in a web application:

```
{
  "character": {
    "name": "Ted Mosby",
    "occupation": "Architect",
    "education": "Wesleyan University",
    "friends": ["Marshall Eriksen", "Lily Aldrin", "Robin Scherbatsky", "Barney Stinson"],
    "interests": ["Architecture", "Teaching", "Finding true love"],
    "favoritePlaces": ["MacLaren's Pub", "The Empire State Building"],
    "quotes": [
      "Sometimes things have to fall apart to make way for better things.",
      "If you're not scared, you're not taking a chance. And if you're not taking a chance, then what the hell are you doing?"
    ]
  }
}
```

This JSON example shows how data can be structured in a clear, readable format for both people and computers. Using jsonify in Flask allows this data to be converted and returned as a response object to the client, with the Content-Type header automatically set to application/json. This simplifies the process. Not only does it make sure that the data is correctly formatted—as as it is in the example above—but it also takes care of setting the necessary HTTP headers.

**Extra Information:**

```
Some common HTTP headers that might be relevant, but are not necessarily set by jsonify:

1. Content-Length: The size of the response body in octets (bytes).
2. Cache-Control: Directives for caching mechanisms in both requests and responses.
3. Set-Cookie: Used to send cookies from the server to the user agent.
4. Access-Control-Allow-Origin: Specifies which domains can access the resources in a cross-origin request.
5. ETag: An identifier for a specific version of a resource, allowing for more efficient caching and resource updates.
6. Expires: The date/time after which the response is considered stale.
7. Last-Modified: The date and time at which the server believes the resource was last modified.
8. Location: Used in redirections or when a new resource has been created. This header indicates the URL to which the request should be redirected.
9. Authorization: Contains the credentials to authenticate a user agent with a server.
10. WWW-Authenticate: Defines the authentication method that should be used to access a resource.

"Setting the necessary HTTP headers" means that the server includes in its response additional metadata about the response itself (like content type, caching policies, authentication requirements, etc.). This information helps the client (e.g., a web browser or another server) to correctly interpret the response, manage caching, handle security, and more.
```

So what exactly did I just say here?

**In simpler terms, using jsonify means:**

- You don't need to manually convert your data (like Python dictionaries or lists) into a JSON string as in our above example; jsonify does that for you.

- You don't have to worry about setting HTTP headers to tell the client that you're sending JSON data. jsonify automatically adds the Content-Type: application/json header, which is essential for web clients (like browsers or other servers) to understand that the data you're sending is in JSON format.

- The data processed by jsonify is wrapped in a Flask Response object. It's a full HTTP response that includes the status code, headers, and the body. This means Flask takes care of the complexities of creating HTTP responses, allowing you to focus on your application logic.

So that's jsonify and now I will explain make_response which is another Flask function, but offers more flexibility compared to jsonify. It allows you to build a response object from various types of return values, not just JSON. You can use it to set additional headers, change the status code of your response, or even attach cookies.

For instance, suppose you want to return a JSON response but also need to set a custom cookie or a specific status code. Make_response lets you do that. You could first use jsonify to convert your data into JSON format and then wrap it with make_response, where you can further manipulate the response by adding headers or changing the status code as needed.

Let's integrate it into the context of our discussion on jsonify and make_response to give a clearer understanding of how make_response can be utilized:

After exploring and using jsonify, I discovered the flexibility of make_response, which broadened my understanding of handling HTTP responses in Flask. Make_response offers more  control over the response, including setting custom status codes and modifying headers. This is useful in scenarios where more than just JSON data needs to be sent back to the client.

Consider if we're developing a web service using Flask, to retrieve character information from a database by their unique ID.

```
from flask import Flask, make_response
from your_model import Character  # Assuming you have a Character model defined

app = Flask(__name__)

@app.route("/characters/<int:id>", methods=["GET"])
def character_by_id(id):
    # Attempt to find the character by its ID
    character = Character.query.filter(Character.id == id).first()

    # If the character isn't found, return an error message with a 404 status code
    if character is None:
        return make_response({"error": "Character not found"}, 404)

    # If Ted Mosby is found, convert his details to a dictionary (excluding some relations if needed) and return it with a 200 status code
    response = make_response(character.to_dict(rules=("-unwanted_relations",)), 200)
    return response
```

**In this example, make_response is used in two key ways:**

- **Handling Not Found Cases:** When the character with the specified ID does not exist in the database, make_response is employed to construct a well-formed HTTP response. This response comprises a JSON body with an error message ("Character not found") and sets the HTTP status code to 404. This status code is crucial for client-side error handling, as it universally indicates that the requested resource could not be found.

```
if character is None:
    return make_response({"error": "Character not found"}, 404)

```

- **Successful Response:** Conversely, when the character is found, the code demonstrates how to prepare and send a successful response back to the client. In this case, character.to_dict(rules=("-unwanted_relations",)) is called to convert the character's details into a dictionary format, excluding certain relations or fields dictated by the provided rules. The make_response function then wraps this dictionary, setting the HTTP status code to 200 to signal a successful operation.

```
response = make_response(character.to_dict(rules=("-unwanted_relations",)), 200)
return response

```

**Using jsonify for Simple JSON Responses**
jsonify is most effective for straightforward cases where your primary goal is to return JSON data to the client. It is a high-level function that abstracts away the details of setting the correct headers and converting your data structure (like a Python dictionary or list) into JSON format. It's the go-to choice when you:

- Need to quickly return JSON data with minimal fuss.

- Are not concerned with setting custom status codes or headers.

- Want to ensure the response has the correct Content-Type: application/json header automatically.

**Example Use Case:** Returning a list of items from a database that the front end will use to display a simple list. The simplicity of jsonify makes it a good fit here.

Make_response is better in scenarios requiring more control over the HTTP response. This includes setting custom HTTP headers, changing the status code, attaching cookies, or even returning different types of responses (not just JSON). It's the tool of choice when you need to:

- Customize the response’s status code based on the outcome of a request. For example, returning a 201 for a successful resource creation or a 404 when a resource cannot be found.

- Add specific headers to a response, such as Cache-Control for caching strategies, or Set-Cookie for session management.

- Return content types other than JSON, like HTML or plain text, while still being able to easily return JSON when necessary.

Choosing between jsonify and make_response in Flask comes down to simplicity versus flexibility. Use jsonify for straightforward JSON responses, and make_response when you need more control over your response, like setting custom status codes and headers.