Skip to content

Commit 94ee932

Browse files
authored
✨ Add ORJSONResponse (fastapi#1065)
* ✨ Add ORJSONResponse * 📝 Add tutorial using ORJSONResponse * ✅ Add test for ORJSONResponse * 📝 Update index.md
1 parent cf760d6 commit 94ee932

File tree

6 files changed

+80
-4
lines changed

6 files changed

+80
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ Used by Starlette:
398398
Used by FastAPI / Starlette:
399399

400400
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
401+
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
401402

402403
You can install all of these with `pip install fastapi[all]`.
403404

docs/advanced/custom-response.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ And if that `Response` has a JSON media type (`application/json`), like is the c
1313
!!! note
1414
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.
1515

16-
## Use `UJSONResponse`
16+
## Use `ORJSONResponse`
1717

18-
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be `UJSONResponse`.
18+
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`.
1919

2020
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
2121

2222
```Python hl_lines="2 7"
23-
{!./src/custom_response/tutorial001.py!}
23+
{!./src/custom_response/tutorial001b.py!}
2424
```
2525

2626
!!! info
@@ -30,6 +30,9 @@ Import the `Response` class (sub-class) you want to use and declare it in the *p
3030

3131
And it will be documented as such in OpenAPI.
3232

33+
!!! tip
34+
The `ORJSONResponse` is currently only available in FastAPI, not in Starlette.
35+
3336
## HTML Response
3437

3538
To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
@@ -134,13 +137,24 @@ Takes some data and returns an `application/json` encoded response.
134137

135138
This is the default response used in **FastAPI**, as you read above.
136139

140+
### `ORJSONResponse`
141+
142+
A fast alternative JSON response using <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, as you read above.
143+
137144
### `UJSONResponse`
138145

139-
An alternative JSON response using `ujson` for faster serialization as you read above.
146+
An alternative JSON response using <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
140147

141148
!!! warning
142149
`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
143150

151+
```Python hl_lines="2 7"
152+
{!./src/custom_response/tutorial001.py!}
153+
```
154+
155+
!!! tip
156+
It's possible that `ORJSONResponse` might be a faster alternative.
157+
144158
### `RedirectResponse`
145159

146160
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ Used by Starlette:
398398
Used by FastAPI / Starlette:
399399

400400
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
401+
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
401402

402403
You can install all of these with `pip install fastapi[all]`.
403404

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from fastapi import FastAPI
2+
from fastapi.responses import ORJSONResponse
3+
4+
app = FastAPI()
5+
6+
7+
@app.get("/items/", response_class=ORJSONResponse)
8+
async def read_items():
9+
return [{"item_id": "Foo"}]

fastapi/responses.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
from starlette.responses import FileResponse # noqa
24
from starlette.responses import HTMLResponse # noqa
35
from starlette.responses import JSONResponse # noqa
@@ -6,3 +8,16 @@
68
from starlette.responses import Response # noqa
79
from starlette.responses import StreamingResponse # noqa
810
from starlette.responses import UJSONResponse # noqa
11+
12+
try:
13+
import orjson
14+
except ImportError: # pragma: nocover
15+
orjson = None # type: ignore
16+
17+
18+
class ORJSONResponse(JSONResponse):
19+
media_type = "application/json"
20+
21+
def render(self, content: Any) -> bytes:
22+
assert orjson is not None, "orjson must be installed to use ORJSONResponse"
23+
return orjson.dumps(content)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from fastapi.testclient import TestClient
2+
3+
from custom_response.tutorial001b import app
4+
5+
client = TestClient(app)
6+
7+
openapi_schema = {
8+
"openapi": "3.0.2",
9+
"info": {"title": "FastAPI", "version": "0.1.0"},
10+
"paths": {
11+
"/items/": {
12+
"get": {
13+
"responses": {
14+
"200": {
15+
"description": "Successful Response",
16+
"content": {"application/json": {"schema": {}}},
17+
}
18+
},
19+
"summary": "Read Items",
20+
"operationId": "read_items_items__get",
21+
}
22+
}
23+
},
24+
}
25+
26+
27+
def test_openapi_schema():
28+
response = client.get("/openapi.json")
29+
assert response.status_code == 200
30+
assert response.json() == openapi_schema
31+
32+
33+
def test_get_custom_response():
34+
response = client.get("/items/")
35+
assert response.status_code == 200
36+
assert response.json() == [{"item_id": "Foo"}]

0 commit comments

Comments
 (0)