]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:sparkles: Add ORJSONResponse (#1065)
authorSebastián Ramírez <tiangolo@gmail.com>
Sun, 1 Mar 2020 21:30:58 +0000 (22:30 +0100)
committerGitHub <noreply@github.com>
Sun, 1 Mar 2020 21:30:58 +0000 (22:30 +0100)
* :sparkles: Add ORJSONResponse

* :memo: Add tutorial using ORJSONResponse

* :white_check_mark: Add test for ORJSONResponse

* :memo: Update index.md

README.md
docs/advanced/custom-response.md
docs/index.md
docs/src/custom_response/tutorial001b.py [new file with mode: 0644]
fastapi/responses.py
tests/test_tutorial/test_custom_response/test_tutorial001b.py [new file with mode: 0644]

index 9f9c440978594b395b46c05afdfc38e9911ab66f..8801c1af57b63965240088b49e4d155a9b588c26 100644 (file)
--- a/README.md
+++ b/README.md
@@ -398,6 +398,7 @@ Used by Starlette:
 Used by FastAPI / Starlette:
 
 * <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
+* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
 
 You can install all of these with `pip install fastapi[all]`.
 
index 5e4504023440aaf882afb75529b0e7b13e2fb790..21e7abee4bcb1b9da3221e1a93980d066aacf516 100644 (file)
@@ -13,14 +13,14 @@ And if that `Response` has a JSON media type (`application/json`), like is the c
 !!! 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 `UJSONResponse`
+## Use `ORJSONResponse`
 
-For example, if you are squeezing performance, you can install and use `ujson` and set the response to be `UJSONResponse`.
+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*.
 
 ```Python hl_lines="2 7"
-{!./src/custom_response/tutorial001.py!}
+{!./src/custom_response/tutorial001b.py!}
 ```
 
 !!! info
@@ -30,6 +30,9 @@ Import the `Response` class (sub-class) you want to use and declare it in the *p
 
     And it will be documented as such in OpenAPI.
 
+!!! tip
+    The `ORJSONResponse` is currently only available in FastAPI, not in Starlette.
+
 ## HTML Response
 
 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.
 
 This is the default response used in **FastAPI**, as you read above.
 
+### `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.
+
 ### `UJSONResponse`
 
-An alternative JSON response using `ujson` for faster serialization as you read above.
+An alternative JSON response using <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
 
 !!! warning
     `ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
 
+```Python hl_lines="2 7"
+{!./src/custom_response/tutorial001.py!}
+```
+
+!!! tip
+    It's possible that `ORJSONResponse` might be a faster alternative.
+
 ### `RedirectResponse`
 
 Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
index 9f9c440978594b395b46c05afdfc38e9911ab66f..8801c1af57b63965240088b49e4d155a9b588c26 100644 (file)
@@ -398,6 +398,7 @@ Used by Starlette:
 Used by FastAPI / Starlette:
 
 * <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
+* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
 
 You can install all of these with `pip install fastapi[all]`.
 
diff --git a/docs/src/custom_response/tutorial001b.py b/docs/src/custom_response/tutorial001b.py
new file mode 100644 (file)
index 0000000..e984603
--- /dev/null
@@ -0,0 +1,9 @@
+from fastapi import FastAPI
+from fastapi.responses import ORJSONResponse
+
+app = FastAPI()
+
+
+@app.get("/items/", response_class=ORJSONResponse)
+async def read_items():
+    return [{"item_id": "Foo"}]
index 69bf5c91c30c3a098249d3b34ef5cee331179197..0aeff61d0f8c8abe21e84509ba92ff7e68a8811d 100644 (file)
@@ -1,3 +1,5 @@
+from typing import Any
+
 from starlette.responses import FileResponse  # noqa
 from starlette.responses import HTMLResponse  # noqa
 from starlette.responses import JSONResponse  # noqa
@@ -6,3 +8,16 @@ from starlette.responses import RedirectResponse  # noqa
 from starlette.responses import Response  # noqa
 from starlette.responses import StreamingResponse  # noqa
 from starlette.responses import UJSONResponse  # noqa
+
+try:
+    import orjson
+except ImportError:  # pragma: nocover
+    orjson = None  # type: ignore
+
+
+class ORJSONResponse(JSONResponse):
+    media_type = "application/json"
+
+    def render(self, content: Any) -> bytes:
+        assert orjson is not None, "orjson must be installed to use ORJSONResponse"
+        return orjson.dumps(content)
diff --git a/tests/test_tutorial/test_custom_response/test_tutorial001b.py b/tests/test_tutorial/test_custom_response/test_tutorial001b.py
new file mode 100644 (file)
index 0000000..08f3728
--- /dev/null
@@ -0,0 +1,36 @@
+from fastapi.testclient import TestClient
+
+from custom_response.tutorial001b import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "FastAPI", "version": "0.1.0"},
+    "paths": {
+        "/items/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Read Items",
+                "operationId": "read_items_items__get",
+            }
+        }
+    },
+}
+
+
+def test_openapi_schema():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+
+
+def test_get_custom_response():
+    response = client.get("/items/")
+    assert response.status_code == 200
+    assert response.json() == [{"item_id": "Foo"}]