Skip to content

🗑️ Deprecate ORJSONResponse and UJSONResponse#14964

Merged
tiangolo merged 3 commits intomasterfrom
deprecate-orjson-ujson
Feb 22, 2026
Merged

🗑️ Deprecate ORJSONResponse and UJSONResponse#14964
tiangolo merged 3 commits intomasterfrom
deprecate-orjson-ujson

Conversation

@tiangolo
Copy link
Member

@tiangolo tiangolo commented Feb 22, 2026

🗑️ Deprecate ORJSONResponse and UJSONResponse

Now that there's better performance by default, with response models: #14962

This removes ujson and orjson from the "fastapi[all]" extras.

To use these responses, ujson or orjson need to be explicitly installed.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 22, 2026

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 22, 2026

Merging this PR will not alter performance

✅ 20 untouched benchmarks


Comparing deprecate-orjson-ujson (c21b0aa) with master (2e62fb1)

Open in CodSpeed

@tiangolo tiangolo marked this pull request as ready for review February 22, 2026 16:32
@tiangolo tiangolo merged commit 48e9835 into master Feb 22, 2026
39 checks passed
@tiangolo tiangolo deleted the deprecate-orjson-ujson branch February 22, 2026 16:35
@remimd
Copy link

remimd commented Feb 22, 2026

With this deprecation, what's the recommended approach for exception handlers that return plain dict bodies?

For example:

@app.exception_handler(ValidationError)
async def validation_error_handler(request: Request, exception: ValidationError) -> Response:
    return ORJSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content={"errors": exception.errors()},
    )

Since exception handlers don't use Pydantic return types, they don't benefit from the Pydantic serialization. Should we just switch to JSONResponse and accept the performance difference, or is there another approach?

@tmdgusya
Copy link

Are there any benchmark or something?

@YuriiMotov
Copy link
Member

With this deprecation, what's the recommended approach for exception handlers that return plain dict bodies?

You can copy the implementation of ORJSONResponse and use it instead of importing from fastapi.
Or, you can return Response(content=ujson.dumps(content, ensure_ascii=False).encode("utf-8")).
Don't forget to add ujson explicit dependency to your project

@eltoder
Copy link

eltoder commented Feb 23, 2026

Are there any benchmark or something?

@tmdgusya I ran benchmarks for my responses (#14299 (comment)). Feel free to plug in yours and check.

@remimd
Copy link

remimd commented Feb 23, 2026

@YuriiMotov thanks for the answer! Though I think deprecating these classes is a mistake, for plain dict serialization, orjson are still significantly faster than stdlib, and that's free performance. Copying the implementation feels like a workaround for removing something that's still useful.

@YuriiMotov
Copy link
Member

@YuriiMotov thanks for the answer! Though I think deprecating these classes is a mistake, for plain dict serialization, orjson are still significantly faster than stdlib, and that's free performance. Copying the implementation feels like a workaround for removing something that's still useful.

Do you mean routes like this:

@app.get("/default")
def get_default() -> dict:
    return {"name": "widget", "price": 9.99}

?

In this case FastAPI will also serialize it with Pydantic, not stdlib:

Details
from unittest.mock import patch

from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: float


app = FastAPI()


@app.get("/default")
def get_default() -> dict:
    return {"name": "widget", "price": 9.99}


client = TestClient(app)


def test_default_response_class_skips_json_dumps():
    """When no response_class is set, the fast path serializes directly to
    JSON bytes via Pydantic's dump_json and never calls json.dumps."""
    with patch(
        "starlette.responses.json.dumps", wraps=__import__("json").dumps
    ) as mock_dumps:
        response = client.get("/default")
    assert response.status_code == 200
    assert response.json() == {"name": "widget", "price": 9.99}
    mock_dumps.assert_not_called()

If you still think we need to de-deprecate these classes, please open a discussion and let's continue discussing it there

@remimd
Copy link

remimd commented Feb 23, 2026

Oh my bad, I didn't realize Pydantic could serialize plain dicts too! In that case, should Pydantic serialization become the default for JSONResponse as well?

Edit: I just started a discussion on this topic #14980

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants