Responses#
This notebook focuses on return information from fastapi handlers.
Check custom reponse offical documentation page.
Container for the examples in this page. Check run application for more details about image that is used for this container.
import os
import json
import requests
from src.rerun_docker import reload_docker_container
local_path = os.path.abspath("./responses_files/app.py")
ans = reload_docker_container(
name="test_container",
image="fastapi_experiment",
command="uvicorn --host 0.0.0.0 --reload app:app",
detach=True,
stdin_open=True,
tty=True,
remove=True,
ports={'8000/tcp': 8000},
volumes={
local_path: {
'bind': '/app.py',
'mode': 'rw'
}
}
)
Single value types#
Consider the case when you are returning types that contain a value like int, float, str and so on.
Unannotated#
APIs return value does not have to be declared.
So in the following example the output data type is not specified and the query is executed without problems.
%%writefile responses_files/app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/divide")
def divide(a: int, b: int):
return a/b
Overwriting responses_files/app.py
response = requests.get("http://localhost:8000/divide?a=10&b=2")
response.text
'5.0'
Annotated#
If you have annotated a type, you must follow it.
In the following example, the GET response function is configured to return an int. However, the API’s computations may result in a float, leading to an error.
%%writefile responses_files/app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/divide")
def divide(a: int, b: int) -> int:
return a/b
Overwriting responses_files/app.py
Here is an example of API input that results in a float data type being returned, causing the API to return an “Internal Server Error.”
response = requests.get("http://localhost:8000/divide?a=1&b=2")
response.text
'Internal Server Error'
However, if you pass values that can be unambiguously reduced to an integer, the API will return the result as an integer.
response = requests.get("http://localhost:8000/divide?a=4&b=2")
response.text
'2'
JSON#
JSON format is a popular method for transferring data over the internet. The most straightforward way to return JSON data is to pass a list or dict as the return value. Find out more in the specific page.
The following cell defines an application that has both handlers that return dictionary and list.
%%writefile responses_files/app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/dict")
def return_dict():
return {
"key1": "value1",
"key2": "value2",
6: 34
}
@app.get("/list")
def return_json():
return [1, 2, 3, "hello", True]
Overwriting responses_files/app.py
Now let’s look at the results for both handlers.
display(requests.get("http://localhost:8000/list").content)
display(requests.get("http://localhost:8000/dict").content)
b'[1,2,3,"hello",true]'
b'{"key1":"value1","key2":"value2","6":34}'
Note that all keys in the dictionary have been converted to strings. So originally 6 was converted to “6”.
It’s important that we include the Content-Type: application/json header in response headers to clearly indicate that the response body contains JSON data.
headers = requests.get("http://localhost:8000/dict").headers
for key, value in headers.items():
print(key, value)
date Mon, 02 Feb 2026 12:30:38 GMT
server uvicorn
content-length 40
content-type application/json
Pydantic models#
You can return instances of the pydantic models - it will convert the output to the corresponding Json file.
The following example shows an application that uses the pydantic model to define what a program must return.
%%writefile responses_files/app.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Output(BaseModel):
a: int
b: str
@app.get("/")
def return_dict() -> Output:
return Output(a=10, b="string value")
Overwriting responses_files/app.py
response = requests.get("http://localhost:8000/")
response.text
'{"a":10,"b":"string value"}'
Exceptions#
In backend development, there are situations where you need to return messages to the user indicating errors during code execution. There are dedicated objects designed for this purpose: HTTPException, RequestValidationError, StarletteHTTPException, and so on. You can even create your custom exceptions. The key feature of these objects is that you must raise them, not return them.
Check:
Handling errors official fastAPI documentation.
The following example defines an API that always raises an HTTPException with a specified message.
%%writefile responses_files/app.py
from fastapi import FastAPI
from fastapi.exceptions import HTTPException
app = FastAPI()
@app.get("/")
def return_dict():
raise HTTPException(404, "Custom not found")
Overwriting responses_files/app.py
The following cell shows the status code and content returned from the API.
response = requests.get("http://localhost:8000")
print("Status code", response.status_code)
print("Content", response.content)
Status code 404
Content b'{"detail":"Custom not found"}'
Input validation#
If the input is invalid, FastAPI returns a 422 response with details describing the incident.
The following cell defines the application that requires the a query parameter to be an integer.
%%writefile responses_files/app.py
from fastapi import FastAPI
from fastapi.exceptions import HTTPException
app = FastAPI()
@app.get("/")
def return_dict(a: int):
raise HTTPException(404, "Custom not found")
Overwriting responses_files/app.py
If the application encounters a value that cannot be interpreted as the integer, the output is displayed in the following cell.
ans = requests.get("http://localhost:8000?a='hello'")
print(ans.status_code)
json.loads(ans.text)
422
{'detail': [{'type': 'int_parsing',
'loc': ['query', 'a'],
'msg': 'Input should be a valid integer, unable to parse string as an integer',
'input': "'hello'",
'url': 'https://errors.pydantic.dev/2.12/v/int_parsing'}]}