This is the default response used in **FastAPI**, as you read above.
+/// note | Technical Details
+
+But if you declare a response model or return type, that will be used directly to serialize the data to JSON, and a response with the right media type for JSON will be returned directly, without using the `JSONResponse` class.
+
+This is the ideal way to get the best performance.
+
+///
+
### `RedirectResponse` { #redirectresponse }
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
### `StreamingResponse` { #streamingresponse }
-Takes an async generator or a normal generator/iterator and streams the response body.
+Takes an async generator or a normal generator/iterator (a function with `yield`) and streams the response body.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
///
-#### Using `StreamingResponse` with file-like objects { #using-streamingresponse-with-file-like-objects }
-
-If you have a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> object (e.g. the object returned by `open()`), you can create a generator function to iterate over that file-like object.
-
-That way, you don't have to read it all first in memory, and you can pass that generator function to the `StreamingResponse`, and return it.
-
-This includes many libraries to interact with cloud storage, video processing, and others.
-
-{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
-
-1. This is the generator function. It's a "generator function" because it contains `yield` statements inside.
-2. By using a `with` block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response.
-3. This `yield from` tells the function to iterate over that thing named `file_like`. And then, for each part iterated, yield that part as coming from this generator function (`iterfile`).
-
- So, it is a generator function that transfers the "generating" work to something else internally.
-
- By doing it this way, we can put it in a `with` block, and that way, ensure that the file-like object is closed after finishing.
-
/// tip
-Notice that here as we are using standard `open()` that doesn't support `async` and `await`, we declare the path operation with normal `def`.
+Instead of returning a `StreamingResponse` directly, you should probably follow the style in [Stream Data](./stream-data.md){.internal-link target=_blank}, it's much more convenient and handles cancellation behind the scenes for you.
+
+If you are streaming JSON Lines, follow the [Stream JSON Lines](../tutorial/stream-json-lines.md){.internal-link target=_blank} tutorial.
///
## Return a `Response` { #return-a-response }
-You can return any `Response` or any sub-class of it.
+You can return a `Response` or any sub-class of it.
/// info
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
-This gives you a lot of flexibility. You can return any data type, override any data declaration or validation, etc.
+This gives you a lot of **flexibility**. You can return any data type, override any data declaration or validation, etc.
+
+It also gives you a lot of **responsibility**. You have to make sure that the data you return is correct, in the correct format, that it can be serialized, etc.
## Using the `jsonable_encoder` in a `Response` { #using-the-jsonable-encoder-in-a-response }
## 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.
+When you declare a [Response Model - Return Type](../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.
+When using a `response_model` or return type, 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`).
+Instead it takes the JSON bytes generated with Pydantic using the response model (or return type) and returns a `Response` with the right media type for JSON directly (`application/json`).
## Notes { #notes }
Then you can use this new class in `response_class=PNGStreamingResponse` in your *path operation function*:
-{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:26] hl[23] *}
+{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Simulate a File { #simulate-a-file }
For example, we can iterate over it to consume its contents, as we could with a file.
-{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:26] hl[3,12:13,25] *}
+{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Technical Details
///
+By using a `with` block, we make sure that the file-like object is closed after the generator function (the function with `yield`) is done. So, after it finishes sending the response.
+
+It wouldn't be that important in this specific example because it's a fake in-memory file (with `io.BytesIO`), but with a real file, it would be important to make sure the file is closed after the work with it is done.
+
### Files and Async { #files-and-async }
In most cases, file-like objects are not compatible with async and await by default.
To avoid blocking the event loop, you can simply declare the *path operation function* with regular `def` instead of `async def`, that way FastAPI will run it on a threadpool worker, to avoid blocking the main loop.
-{* ../../docs_src/stream_data/tutorial002_py310.py ln[29:32] hl[30] *}
+{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip
If you need to call blocking code from inside of an async function, or an async function from inside of a blocking function, you could use <a href="https://asyncer.tiangolo.com" class="external-link" target="_blank">Asyncer</a>, a sibling library to FastAPI.
///
+
+### `yield from` { #yield-from }
+
+When you are iterating over something, like a file-like object, and then you are doing `yield` for each item, you could also use `yield from` to yield each item directly and skip the `for` loop.
+
+This is not particular to FastAPI, it's just Python, but it's a nice trick to know. 😎
+
+{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
@app.get("/image/stream", response_class=PNGStreamingResponse)
async def stream_image() -> AsyncIterable[bytes]:
- for chunk in read_image():
- yield chunk
+ with read_image() as image_file:
+ for chunk in image_file:
+ yield chunk
@app.get("/image/stream-no-async", response_class=PNGStreamingResponse)
def stream_image_no_async() -> Iterable[bytes]:
- for chunk in read_image():
- yield chunk
+ with read_image() as image_file:
+ for chunk in image_file:
+ yield chunk
+
+
+@app.get("/image/stream-no-async-yield-from", response_class=PNGStreamingResponse)
+def stream_image_no_async_yield_from() -> Iterable[bytes]:
+ with read_image() as image_file:
+ yield from image_file
@app.get("/image/stream-no-annotation", response_class=PNGStreamingResponse)
async def stream_image_no_annotation():
- for chunk in read_image():
- yield chunk
+ with read_image() as image_file:
+ for chunk in image_file:
+ yield chunk
@app.get("/image/stream-no-async-no-annotation", response_class=PNGStreamingResponse)
def stream_image_no_async_no_annotation():
- for chunk in read_image():
- yield chunk
+ with read_image() as image_file:
+ for chunk in image_file:
+ yield chunk
[
"/image/stream",
"/image/stream-no-async",
+ "/image/stream-no-async-yield-from",
"/image/stream-no-annotation",
"/image/stream-no-async-no-annotation",
],
},
}
},
+ "/image/stream-no-async-yield-from": {
+ "get": {
+ "summary": "Stream Image No Async Yield From",
+ "operationId": "stream_image_no_async_yield_from_image_stream_no_async_yield_from_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "image/png": {"schema": {"type": "string"}}
+ },
+ }
+ },
+ }
+ },
"/image/stream-no-annotation": {
"get": {
"summary": "Stream Image No Annotation",