# Custom Response - HTML, Stream, File, others { #custom-response-html-stream-file-others }
-By default, **FastAPI** will return the responses using `JSONResponse`.
+By default, **FastAPI** will return JSON responses.
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
The contents that you return from your *path operation function* will be put inside of that `Response`.
-And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.
-
/// note
If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.
///
-## Use `ORJSONResponse` { #use-orjsonresponse }
-
-For example, if you are squeezing performance, you can install and use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> and set the response to be `ORJSONResponse`.
-
-Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
+## JSON Responses { #json-responses }
-For large responses, returning a `Response` directly is much faster than returning a dictionary.
+By default FastAPI returns JSON responses.
-This is because by default, FastAPI will inspect every item inside and make sure it is serializable as JSON, using the same [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} explained in the tutorial. This is what allows you to return **arbitrary objects**, for example database models.
+If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.
-But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class.
-
-{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
-
-/// info
-
-The parameter `response_class` will also be used to define the "media type" of the response.
-
-In this case, the HTTP header `Content-Type` will be set to `application/json`.
-
-And it will be documented as such in OpenAPI.
+If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.
-///
+If you declare a `response_class` with a JSON media type (`application/json`), like is the case with the `JSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. But the data won't be serialized to JSON bytes with Pydantic, instead it will be converted with the `jsonable_encoder` and then passed to the `JSONResponse` class, which will serialize it to bytes using the standard JSON library in Python.
-/// tip
+### JSON Performance { #json-performance }
-The `ORJSONResponse` is only available in FastAPI, not in Starlette.
+In short, if you want the maximum performance, use a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} and don't declare a `response_class` in the *path operation decorator*.
-///
+{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML Response { #html-response }
This is the default response used in **FastAPI**, as you read above.
-### `ORJSONResponse` { #orjsonresponse }
-
-A fast alternative JSON response using <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, as you read above.
-
-/// info
-
-This requires installing `orjson` for example with `pip install orjson`.
-
-///
-
-### `UJSONResponse` { #ujsonresponse }
-
-An alternative JSON response using <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
-
-/// info
-
-This requires installing `ujson` for example with `pip install ujson`.
-
-///
-
-/// warning
-
-`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
-
-///
-
-{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
-
-/// tip
-
-It's possible that `ORJSONResponse` might be a faster alternative.
-
-///
-
### `RedirectResponse` { #redirectresponse }
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
You can create your own custom response class, inheriting from `Response` and using it.
-For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, but with some custom settings not used in the included `ORJSONResponse` class.
+For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> with some settings.
Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`.
Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉
+### `orjson` or Response Model { #orjson-or-response-model }
+
+If what you are looking for is performance, you are probably better off using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than an `orjson` response.
+
+With a response model, FastAPI will use Pydantic to serialize the data to JSON, without using intermediate steps, like converting it with `jsonable_encoder`, which would happen in any other case.
+
+And under the hood, Pydantic uses the same underlying Rust mechanisms as `orjson` to serialize to JSON, so you will already get the best performance with a response model.
+
## Default response class { #default-response-class }
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
The parameter that defines this is `default_response_class`.
-In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
+In the example below, **FastAPI** will use `HTMLResponse` by default, in all *path operations*, instead of JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
-By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
+If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.
-Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a `JSONResponse` that would be used to send the response to the client.
+If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.
-But you can return a `JSONResponse` directly from your *path operations*.
+You could also create a `JSONResponse` directly and return it.
-It might be useful, for example, to return custom headers or cookies.
+/// tip
+
+You will normally have much better performance using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than returning a `JSONResponse` directly, as that way it serializes the data using Pydantic, in Rust.
+
+///
## Return a `Response` { #return-a-response }
-In fact, you can return any `Response` or any sub-class of it.
+You can return any `Response` or any sub-class of it.
-/// tip
+/// info
`JSONResponse` itself is a sub-class of `Response`.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
+## How a Response Model Works { #how-a-response-model-works }
+
+When you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} in a path operation, **FastAPI** will use it to serialize the data to JSON, using Pydantic.
+
+{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
+
+As that will happen on the Rust side, the performance will be much better than if it was done with regular Python and the `JSONResponse` class.
+
+When using a response model FastAPI won't use the `jsonable_encoder` to convert the data (which would be slower) nor the `JSONResponse` class.
+
+Instead it takes the JSON bytes generated with Pydantic using the response model and returns a `Response` with the right media type for JSON directly (`application/json`).
+
## Notes { #notes }
When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.
To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
+## Optimize Response Performance - Response Model - Return Type { #optimize-response-performance-response-model-return-type }
+
+To optimize performance when returning JSON data, use a return type or response model, that way Pydantic will handle the serialization to JSON on the Rust side, without going through Python. Read more in the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
+
## Documentation Tags - OpenAPI { #documentation-tags-openapi }
To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
* Add a **JSON Schema** for the response, in the OpenAPI *path operation*.
* This will be used by the **automatic docs**.
* It will also be used by automatic client code generation tools.
+* **Serialize** the returned data to JSON using Pydantic, which is written in **Rust**, so it will be **much faster**.
But most importantly:
from fastapi import FastAPI
-from fastapi.responses import ORJSONResponse
+from fastapi.responses import HTMLResponse
-app = FastAPI(default_response_class=ORJSONResponse)
+app = FastAPI(default_response_class=HTMLResponse)
@app.get("/items/")
async def read_items():
- return [{"item_id": "Foo"}]
+ return "<h1>Items</h1><p>This is a list of items.</p>"
exclude_none=exclude_none,
)
+ def serialize_json(
+ self,
+ value: Any,
+ *,
+ include: IncEx | None = None,
+ exclude: IncEx | None = None,
+ by_alias: bool = True,
+ exclude_unset: bool = False,
+ exclude_defaults: bool = False,
+ exclude_none: bool = False,
+ ) -> bytes:
+ # What calls this code passes a value that already called
+ # self._type_adapter.validate_python(value)
+ # This uses Pydantic's dump_json() which serializes directly to JSON
+ # bytes in one pass (via Rust), avoiding the intermediate Python dict
+ # step of dump_python(mode="json") + json.dumps().
+ return self._type_adapter.dump_json(
+ value,
+ include=include,
+ exclude=exclude,
+ by_alias=by_alias,
+ exclude_unset=exclude_unset,
+ exclude_defaults=exclude_defaults,
+ exclude_none=exclude_none,
+ )
+
def __hash__(self) -> int:
# Each ModelField is unique for our purposes, to allow making a dict from
# ModelField to its JSON Schema.
exclude_none: bool = False,
is_coroutine: bool = True,
endpoint_ctx: EndpointContext | None = None,
+ dump_json: bool = False,
) -> Any:
if field:
if is_coroutine:
body=response_content,
endpoint_ctx=ctx,
)
-
- return field.serialize(
+ serializer = field.serialize_json if dump_json else field.serialize
+ return serializer(
value,
include=include,
exclude=exclude,
response_args["status_code"] = current_status_code
if solved_result.response.status_code:
response_args["status_code"] = solved_result.response.status_code
+ # Use the fast path (dump_json) when no custom response
+ # class was set and a response field with a TypeAdapter
+ # exists. Serializes directly to JSON bytes via Pydantic's
+ # Rust core, skipping the intermediate Python dict +
+ # json.dumps() step.
+ use_dump_json = response_field is not None and isinstance(
+ response_class, DefaultPlaceholder
+ )
content = await serialize_response(
field=response_field,
response_content=raw_response,
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
endpoint_ctx=endpoint_ctx,
+ dump_json=use_dump_json,
)
- response = actual_response_class(content, **response_args)
+ if use_dump_json:
+ response = Response(
+ content=content,
+ media_type="application/json",
+ **response_args,
+ )
+ else:
+ response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
response.headers.raw.extend(solved_result.response.headers.raw)
--- /dev/null
+from unittest.mock import patch
+
+from fastapi import FastAPI
+from fastapi.responses import JSONResponse
+from fastapi.testclient import TestClient
+from pydantic import BaseModel
+
+
+class Item(BaseModel):
+ name: str
+ price: float
+
+
+app = FastAPI()
+
+
+@app.get("/default")
+def get_default() -> Item:
+ return Item(name="widget", price=9.99)
+
+
+@app.get("/explicit", response_class=JSONResponse)
+def get_explicit() -> Item:
+ return Item(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()
+
+
+def test_explicit_response_class_uses_json_dumps():
+ """When response_class is explicitly set to JSONResponse, the normal path
+ is used and json.dumps is called via JSONResponse.render()."""
+ with patch(
+ "starlette.responses.json.dumps", wraps=__import__("json").dumps
+ ) as mock_dumps:
+ response = client.get("/explicit")
+ assert response.status_code == 200
+ assert response.json() == {"name": "widget", "price": 9.99}
+ mock_dumps.assert_called_once()
name="client",
params=[
pytest.param("tutorial001_py310"),
- pytest.param("tutorial010_py310"),
],
)
def get_client(request: pytest.FixtureRequest):
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial010_py310"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.custom_response.{request.param}")
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_custom_response(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.text == snapshot("<h1>Items</h1><p>This is a list of items.</p>")
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "text/html": {"schema": {"type": "string"}}
+ },
+ }
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ }
+ }
+ },
+ }
+ )