## FastAPI Responses
-There are a couple of custom FastAPI response classes, you can use them to optimize JSON performance.
+There were a couple of custom FastAPI response classes that were intended to optimize JSON performance.
+
+However, they are now deprecated as you will now get better performance by using a [Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/).
+
+That way, Pydantic will serialize the data into JSON bytes on the Rust side, which will achieve better performance than these custom JSON responses.
+
+Read more about it in [Custom Response - HTML, Stream, File, others - `orjson` or Response Model](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model).
::: fastapi.responses.UJSONResponse
options:
from typing import Any
+from fastapi.exceptions import FastAPIDeprecationWarning
from starlette.responses import FileResponse as FileResponse # noqa
from starlette.responses import HTMLResponse as HTMLResponse # noqa
from starlette.responses import JSONResponse as JSONResponse # noqa
from starlette.responses import RedirectResponse as RedirectResponse # noqa
from starlette.responses import Response as Response # noqa
from starlette.responses import StreamingResponse as StreamingResponse # noqa
+from typing_extensions import deprecated
try:
import ujson
orjson = None # type: ignore
+@deprecated(
+ "UJSONResponse is deprecated, FastAPI now serializes data directly to JSON "
+ "bytes via Pydantic when a return type or response model is set, which is "
+ "faster and doesn't need a custom response class. Read more in the FastAPI "
+ "docs: https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model "
+ "and https://fastapi.tiangolo.com/tutorial/response-model/",
+ category=FastAPIDeprecationWarning,
+ stacklevel=2,
+)
class UJSONResponse(JSONResponse):
- """
- JSON response using the high-performance ujson library to serialize data to JSON.
+ """JSON response using the ujson library to serialize data to JSON.
+
+ **Deprecated**: `UJSONResponse` is deprecated. FastAPI now serializes data
+ directly to JSON bytes via Pydantic when a return type or response model is
+ set, which is faster and doesn't need a custom response class.
+
+ Read more in the
+ [FastAPI docs for Custom Response](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model)
+ and the
+ [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
- Read more about it in the
- [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
+ **Note**: `ujson` is not included with FastAPI and must be installed
+ separately, e.g. `pip install ujson`.
"""
def render(self, content: Any) -> bytes:
return ujson.dumps(content, ensure_ascii=False).encode("utf-8")
+@deprecated(
+ "ORJSONResponse is deprecated, FastAPI now serializes data directly to JSON "
+ "bytes via Pydantic when a return type or response model is set, which is "
+ "faster and doesn't need a custom response class. Read more in the FastAPI "
+ "docs: https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model "
+ "and https://fastapi.tiangolo.com/tutorial/response-model/",
+ category=FastAPIDeprecationWarning,
+ stacklevel=2,
+)
class ORJSONResponse(JSONResponse):
- """
- JSON response using the high-performance orjson library to serialize data to JSON.
+ """JSON response using the orjson library to serialize data to JSON.
+
+ **Deprecated**: `ORJSONResponse` is deprecated. FastAPI now serializes data
+ directly to JSON bytes via Pydantic when a return type or response model is
+ set, which is faster and doesn't need a custom response class.
+
+ Read more in the
+ [FastAPI docs for Custom Response](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model)
+ and the
+ [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
- Read more about it in the
- [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
+ **Note**: `orjson` is not included with FastAPI and must be installed
+ separately, e.g. `pip install orjson`.
"""
def render(self, content: Any) -> bytes:
"itsdangerous >=1.1.0",
# For Starlette's schema generation, would not be used with FastAPI
"pyyaml >=5.3.1",
- # For UJSONResponse
- "ujson >=5.8.0",
- # For ORJSONResponse
- "orjson >=3.9.3",
# To validate email fields
"email-validator >=2.0.0",
# Uvicorn with uvloop
docs-tests = [
"httpx >=0.23.0,<1.0.0",
"ruff >=0.14.14",
+ # For UJSONResponse
+ "ujson >=5.8.0",
+ # For ORJSONResponse
+ "orjson >=3.9.3",
]
github-actions = [
"httpx >=0.27.0,<1.0.0",
--- /dev/null
+import warnings
+
+import pytest
+from fastapi import FastAPI
+from fastapi.exceptions import FastAPIDeprecationWarning
+from fastapi.responses import ORJSONResponse, UJSONResponse
+from fastapi.testclient import TestClient
+from pydantic import BaseModel
+
+
+class Item(BaseModel):
+ name: str
+ price: float
+
+
+# ORJSON
+
+
+def _make_orjson_app() -> FastAPI:
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ app = FastAPI(default_response_class=ORJSONResponse)
+
+ @app.get("/items")
+ def get_items() -> Item:
+ return Item(name="widget", price=9.99)
+
+ return app
+
+
+def test_orjson_response_returns_correct_data():
+ app = _make_orjson_app()
+ client = TestClient(app)
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ response = client.get("/items")
+ assert response.status_code == 200
+ assert response.json() == {"name": "widget", "price": 9.99}
+
+
+def test_orjson_response_emits_deprecation_warning():
+ with pytest.warns(FastAPIDeprecationWarning, match="ORJSONResponse is deprecated"):
+ ORJSONResponse(content={"hello": "world"})
+
+
+# UJSON
+
+
+def _make_ujson_app() -> FastAPI:
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ app = FastAPI(default_response_class=UJSONResponse)
+
+ @app.get("/items")
+ def get_items() -> Item:
+ return Item(name="widget", price=9.99)
+
+ return app
+
+
+def test_ujson_response_returns_correct_data():
+ app = _make_ujson_app()
+ client = TestClient(app)
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ response = client.get("/items")
+ assert response.status_code == 200
+ assert response.json() == {"name": "widget", "price": 9.99}
+
+
+def test_ujson_response_emits_deprecation_warning():
+ with pytest.warns(FastAPIDeprecationWarning, match="UJSONResponse is deprecated"):
+ UJSONResponse(content={"hello": "world"})
+import warnings
+
from fastapi import FastAPI
+from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.responses import ORJSONResponse
from fastapi.testclient import TestClient
from sqlalchemy.sql.elements import quoted_name
-app = FastAPI(default_response_class=ORJSONResponse)
+with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/orjson_non_str_keys")
def test_orjson_non_str_keys():
- with client:
- response = client.get("/orjson_non_str_keys")
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ with client:
+ response = client.get("/orjson_non_str_keys")
assert response.json() == {"msg": "Hello World", "1": 1}
return client
+@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_get_custom_response(client: TestClient):
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}]
+@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
+import warnings
+
+import pytest
+from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from docs_src.custom_response.tutorial001b_py310 import app
+with warnings.catch_warnings():
+ warnings.simplefilter("ignore", FastAPIDeprecationWarning)
+ from docs_src.custom_response.tutorial001b_py310 import app
client = TestClient(app)
+@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_get_custom_response():
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}]
+@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
{ name = "httpx" },
{ name = "itsdangerous" },
{ name = "jinja2" },
- { name = "orjson" },
{ name = "pydantic-extra-types" },
{ name = "pydantic-settings" },
{ name = "python-multipart" },
{ name = "pyyaml" },
- { name = "ujson" },
{ name = "uvicorn", extra = ["standard"] },
]
standard = [
{ name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] },
{ name = "mypy" },
+ { name = "orjson" },
{ name = "pillow" },
{ name = "playwright" },
{ name = "prek" },
{ name = "typer" },
{ name = "types-orjson" },
{ name = "types-ujson" },
+ { name = "ujson" },
]
docs = [
{ name = "black" },
{ name = "mkdocs-material" },
{ name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] },
+ { name = "orjson" },
{ name = "pillow" },
{ name = "python-slugify" },
{ name = "pyyaml" },
{ name = "ruff" },
{ name = "typer" },
+ { name = "ujson" },
]
docs-tests = [
{ name = "httpx" },
+ { name = "orjson" },
{ name = "ruff" },
+ { name = "ujson" },
]
github-actions = [
{ name = "httpx" },
{ name = "httpx" },
{ name = "inline-snapshot" },
{ name = "mypy" },
+ { name = "orjson" },
{ name = "pwdlib", extra = ["argon2"] },
{ name = "pyjwt" },
{ name = "pytest" },
{ name = "strawberry-graphql" },
{ name = "types-orjson" },
{ name = "types-ujson" },
+ { name = "ujson" },
]
translations = [
{ name = "gitpython" },
{ name = "jinja2", marker = "extra == 'all'", specifier = ">=3.1.5" },
{ name = "jinja2", marker = "extra == 'standard'", specifier = ">=3.1.5" },
{ name = "jinja2", marker = "extra == 'standard-no-fastapi-cloud-cli'", specifier = ">=3.1.5" },
- { name = "orjson", marker = "extra == 'all'", specifier = ">=3.9.3" },
{ name = "pydantic", specifier = ">=2.7.0" },
{ name = "pydantic-extra-types", marker = "extra == 'all'", specifier = ">=2.0.0" },
{ name = "pydantic-extra-types", marker = "extra == 'standard'", specifier = ">=2.0.0" },
{ name = "starlette", specifier = ">=0.40.0,<1.0.0" },
{ name = "typing-extensions", specifier = ">=4.8.0" },
{ name = "typing-inspection", specifier = ">=0.4.2" },
- { name = "ujson", marker = "extra == 'all'", specifier = ">=5.8.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.12.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.12.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'standard-no-fastapi-cloud-cli'", specifier = ">=0.12.0" },
{ name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
{ name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
{ name = "mypy", specifier = ">=1.14.1" },
+ { name = "orjson", specifier = ">=3.9.3" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "playwright", specifier = ">=1.57.0" },
{ name = "prek", specifier = ">=0.2.22" },
{ name = "typer", specifier = ">=0.21.1" },
{ name = "types-orjson", specifier = ">=3.6.2" },
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
+ { name = "ujson", specifier = ">=5.8.0" },
]
docs = [
{ name = "black", specifier = ">=25.1.0" },
{ name = "mkdocs-material", specifier = ">=9.7.0" },
{ name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
{ name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
+ { name = "orjson", specifier = ">=3.9.3" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "python-slugify", specifier = ">=8.0.4" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
{ name = "ruff", specifier = ">=0.14.14" },
{ name = "typer", specifier = ">=0.21.1" },
+ { name = "ujson", specifier = ">=5.8.0" },
]
docs-tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
+ { name = "orjson", specifier = ">=3.9.3" },
{ name = "ruff", specifier = ">=0.14.14" },
+ { name = "ujson", specifier = ">=5.8.0" },
]
github-actions = [
{ name = "httpx", specifier = ">=0.27.0,<1.0.0" },
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "inline-snapshot", specifier = ">=0.21.1" },
{ name = "mypy", specifier = ">=1.14.1" },
+ { name = "orjson", specifier = ">=3.9.3" },
{ name = "pwdlib", extras = ["argon2"], specifier = ">=0.2.1" },
{ name = "pyjwt", specifier = ">=2.9.0" },
{ name = "pytest", specifier = ">=9.0.0" },
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
{ name = "types-orjson", specifier = ">=3.6.2" },
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
+ { name = "ujson", specifier = ">=5.8.0" },
]
translations = [
{ name = "gitpython", specifier = ">=3.1.46" },