Reputation: 1654
I am looking to learn how to make a more robust API in Python using pythonic methods. My goal is to achieve a more strict API response, to reduce the amount of parsing errors on our frontend and increase standardisation for response types (error states, etc).
For the sake of argument, I am using Python 3.11 and Flask. Below is sample code:
from flask import Flask, jsonify
from typing import TypedDict, Any, assert_type, Union
app = Flask(__name__)
# This is our forced API Response that we want to type check.
# Simplified Union data for argument's sake.
class APIResponse(TypedDict):
success: bool
data: Union[dict, list]
@app.get("/")
def index():
response = {
"success": True,
"data": { "hello": "World!" }
}
assert_type(response, APIResponse)
return jsonify(response)
if __name__ == "__main__":
app.run(debug=True)
No errors, as expected. But when changing response to the following:
{
"success": "notABool",
"dat": { "hello", "World!" } # a Set, does not satisfy our type union, an easy (but platonic) mistake
}
I also get no errors (warnings) showing up in my IDE.
I've read the following to try to find answers, but some assume MyPy and others create really lax static type checking (dict is a dict, therefore is good):
I understand I could use PyDantic, but I would rather not wait for runtime errors to find out that an API response was not implemented correctly.
Thanks for any help.
Upvotes: 0
Views: 118
Reputation: 752
To check type or format all api response. You can use register_error_handler
and after_request
. Example:
class HandleSuccess:
@classmethod
def after_request_handler(cls, response):
# do here all what you need with success responses
# assert_type(response, APIResponse)
print('good response %s' % response.status_code)
return response
class HandleError:
@classmethod
def logger_error(cls, e):
app.logger.error("ERROR: %s" % str(e), exc_info=True)
@classmethod
def handle(cls, error_return):
cls.logger_error(error_return)
return jsonify(success=False, error=str(error_return))
In app.py:
app.register_error_handler(Exception, HandleError.handle)
app.after_request(HandleSuccess.after_request_handler)
Now, you can handle api without manual check in each api
Upvotes: 0