JSON#

JSON format is a widely used method for data exchange over the internet. This page explores various approaches to crafting JSON responses within the FastAPI framework.

import requests

!docker run --rm -itd\
    --name fastapi_json_responses\
    -v ./json_files/app.py:/app.py\
    -p 8000:8000 \
    fastapi_experiment \
    uvicorn --host 0.0.0.0 --reload app:app
64f8096c46f92f2392710e94830f4e5a57b708d0b7d7b85a7b77a6c8d5d1d274

Note: Remember to stop the container after you’re finished.

!docker stop fastapi_json_responses
fastapi_json_responses

String as json#

When building an API that primarily returns JSON data, it’s common to encounter scenarios where you need to provide pre-serialized JSON responses. In such cases, you can simply return a string containing the serialized JSON, which the browser will interpret as JSON.


Let’s consider an example where our API simply returns the string “hello”.

%%writefile json_files/app.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def get() -> str:
    return 'hello'
Overwriting json_files/app.py

Now let’s response of the endpoint and check it’s headers.

for key, item in requests.get("http://localhost:8000").headers.items():
    print(key, item)
date Fri, 16 Aug 2024 08:35:28 GMT
server uvicorn
content-length 7
content-type application/json

The header Content-Type: application/json indicates that the API has returned JSON data.

JSONResponse object#

FastAPI provides a special class, fastapi.responses.JSONResponse, which allows for more fine-grained control over JSON responses. The following cell shows the docstring for this object.

from fastapi.responses import JSONResponse
JSONResponse?
Init signature:
JSONResponse(
    content: 'typing.Any',
    status_code: 'int' = 200,
    headers: 'typing.Mapping[str, str] | None' = None,
    media_type: 'str | None' = None,
    background: 'BackgroundTask | None' = None,
) -> 'None'
Docstring:      <no docstring>
File:           ~/.local/lib/python3.10/site-packages/starlette/responses.py
Type:           type
Subclasses:     UJSONResponse, ORJSONResponse

The following cell defines a FastAPI application that utilizes JSONResponse as the object to be returned. It defines content, status_code, and headers, which we will explore to see how they influence the response.

%%writefile json_files/app.py
from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/")
def get() -> JSONResponse:
    return JSONResponse(
        content={"key": "value"}, 
        status_code=255, 
        headers={"name": "Fedor", "surname": "Kobak"}
    )
Overwriting json_files/app.py

The following cell sends a request to the newly created API and prints the content, status_code, and headers of the response.

response = requests.get("http://localhost:8000/")

print("content", response.content)
print("status code", response.status_code)
print("headers", response.headers)
content b'{"key":"value"}'
status code 255
headers {'date': 'Tue, 20 Aug 2024 12:04:25 GMT', 'server': 'uvicorn', 'name': 'Fedor', 'surname': 'Kobak', 'content-length': '15', 'content-type': 'application/json'}

So, the content and status code are exactly as we specified. The headers also include the specified key/value pairs, along with the necessary ones.