mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-25 07:08:11 -05:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
025b38df40 | ||
|
|
ac60cba75f | ||
|
|
94ee932351 | ||
|
|
cf760d6802 | ||
|
|
c65fdc4bed | ||
|
|
0ac9b3ee5c | ||
|
|
f2bd2c44e2 | ||
|
|
f0beab1778 | ||
|
|
95f2dc065e | ||
|
|
4e8080f290 | ||
|
|
fbbed6fe81 | ||
|
|
2d5a5d0d9e | ||
|
|
d4ddf4e62a | ||
|
|
cb25dab986 | ||
|
|
7aa1628336 | ||
|
|
1dbd3d7aa7 | ||
|
|
24e73e01c7 | ||
|
|
e26f94018c | ||
|
|
1ce67887b9 | ||
|
|
8e0200607f | ||
|
|
bd407ca705 | ||
|
|
48b5ef1681 | ||
|
|
afad59dfbb | ||
|
|
0f9be1d2e7 | ||
|
|
48c2406495 | ||
|
|
9958d93120 | ||
|
|
b63512cd92 | ||
|
|
74c4d1c1db | ||
|
|
7ccd81f706 | ||
|
|
1da8d3f1e6 | ||
|
|
92016da962 | ||
|
|
9c3c9b6e78 | ||
|
|
687871e46a | ||
|
|
3c1803897f | ||
|
|
1c3289f115 | ||
|
|
35d41adc7b | ||
|
|
9a3dd91030 | ||
|
|
017eae63b1 | ||
|
|
8af4454251 | ||
|
|
adf252768c | ||
|
|
3705bdefd5 | ||
|
|
25e94d6344 | ||
|
|
26c345b766 | ||
|
|
9ce50b4684 | ||
|
|
66954c7ded | ||
|
|
e0c3519b94 | ||
|
|
d91b2b3ee8 | ||
|
|
1623b26855 | ||
|
|
9573130630 | ||
|
|
2e0a102565 | ||
|
|
636ce6b3f7 | ||
|
|
1fd1e8733a | ||
|
|
44b45caf65 | ||
|
|
e6da96fbb7 | ||
|
|
c425509d57 | ||
|
|
d8451f75a4 | ||
|
|
a448bd63bd | ||
|
|
27fb2b358c | ||
|
|
68723d5291 | ||
|
|
70bdade23b | ||
|
|
4f964939a1 | ||
|
|
55afb70b37 | ||
|
|
b307d38897 | ||
|
|
a085898309 | ||
|
|
3b40c557ce | ||
|
|
75a07f24bf | ||
|
|
7cea84b74c |
29
.github/workflows/deploy-docs.yml
vendored
Normal file
29
.github/workflows/deploy-docs.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Build and Deploy to Netlify
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.7"
|
||||
- name: Install Flit
|
||||
run: python3.7 -m pip install flit
|
||||
- name: Install docs extras
|
||||
run: python3.7 -m flit install --extras doc
|
||||
- name: Build MkDocs
|
||||
run: python3.7 -m mkdocs build
|
||||
- name: Deploy to Netlify
|
||||
uses: nwtgck/actions-netlify@v1.0.3
|
||||
with:
|
||||
publish-dir: './site'
|
||||
production-branch: master
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
@@ -319,7 +319,7 @@ Coming back to the previous code example, **FastAPI** will:
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that is has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
* All this would also work for deeply nested JSON objects.
|
||||
* Convert from and to JSON automatically.
|
||||
@@ -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]`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
|
||||
By default, **FastAPI** will return the responses using a `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
|
||||
|
||||
It will use the default status code or the one you set in your *path operation*.
|
||||
|
||||
@@ -12,7 +12,7 @@ But you also want it to accept new items. And when the items didn't exist before
|
||||
|
||||
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
|
||||
|
||||
```Python hl_lines="2 20"
|
||||
```Python hl_lines="2 19"
|
||||
{!./src/additional_status_codes/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -23,8 +23,13 @@ To achieve that, import `JSONResponse`, and return your content there directly,
|
||||
|
||||
Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`).
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `status`.
|
||||
|
||||
## OpenAPI and API docs
|
||||
|
||||
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know before hand what you are going to return.
|
||||
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know beforehand what you are going to return.
|
||||
|
||||
But you can document that in your code, using: [Additional Responses](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
!!! warning
|
||||
This is, more or less, an "advanced" chapter.
|
||||
|
||||
If you are just starting with **FastAPI** you might want to skip this chapter and come back to it later.
|
||||
|
||||
## Parameterized dependencies
|
||||
|
||||
All the dependencies we have seen are a fixed function or class.
|
||||
|
||||
@@ -25,13 +25,16 @@ And an `APIRoute` subclass to use that custom request class.
|
||||
|
||||
### Create a custom `GzipRequest` class
|
||||
|
||||
!!! tip
|
||||
This is a toy example to demonstrate how it works, if you need Gzip support, you can use the provided [`GzipMiddleware`](./middleware.md#gzipmiddleware){.internal-link target=_blank}.
|
||||
|
||||
First, we create a `GzipRequest` class, which will overwrite the `Request.body()` method to decompress the body in the presence of an appropriate header.
|
||||
|
||||
If there's no `gzip` in the header, it will not try to decompress the body.
|
||||
|
||||
That way, the same route class can handle gzip compressed or uncompressed requests.
|
||||
|
||||
```Python hl_lines="10 11 12 13 14 15 16 17"
|
||||
```Python hl_lines="8 9 10 11 12 13 14 15"
|
||||
{!./src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -45,7 +48,7 @@ This method returns a function. And that function is what will receive a request
|
||||
|
||||
Here we use it to create a `GzipRequest` from the original request.
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25 26 27 28"
|
||||
```Python hl_lines="18 19 20 21 22 23 24 25 26"
|
||||
{!./src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -79,13 +82,13 @@ We can also use this same approach to access the request body in an exception ha
|
||||
|
||||
All we need to do is handle the request inside a `try`/`except` block:
|
||||
|
||||
```Python hl_lines="15 17"
|
||||
```Python hl_lines="13 15"
|
||||
{!./src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
|
||||
|
||||
```Python hl_lines="18 19 20"
|
||||
```Python hl_lines="16 17 18"
|
||||
{!./src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -93,12 +96,12 @@ If an exception occurs, the`Request` instance will still be in scope, so we can
|
||||
|
||||
You can also set the `route_class` parameter of an `APIRouter`:
|
||||
|
||||
```Python hl_lines="28"
|
||||
```Python hl_lines="26"
|
||||
{!./src/custom_request_and_route/tutorial003.py!}
|
||||
```
|
||||
|
||||
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
|
||||
|
||||
```Python hl_lines="15 16 17 18 19 20 21 22"
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20"
|
||||
{!./src/custom_request_and_route/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
!!! warning
|
||||
This is a rather advanced topic.
|
||||
|
||||
If you are starting with **FastAPI**, you might not need this.
|
||||
|
||||
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`.
|
||||
By default, **FastAPI** will return the responses using `JSONResponse`.
|
||||
|
||||
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type`).
|
||||
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI).
|
||||
|
||||
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
|
||||
|
||||
@@ -18,19 +13,16 @@ 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 Starlette's `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!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
||||
|
||||
!!! info
|
||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||
|
||||
@@ -38,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`.
|
||||
@@ -49,9 +44,6 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
||||
{!./src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
||||
|
||||
!!! info
|
||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||
|
||||
@@ -59,7 +51,7 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
||||
|
||||
And it will be documented as such in OpenAPI.
|
||||
|
||||
### Return a Starlette `Response`
|
||||
### Return a `Response`
|
||||
|
||||
As seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}, you can also override the response directly in your *path operation*, by returning it.
|
||||
|
||||
@@ -89,14 +81,126 @@ For example, it could be something like:
|
||||
{!./src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
In this example, the function `generate_html_response()` already generates a Starlette `Response` instead of the HTML in a `str`.
|
||||
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
|
||||
|
||||
By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior.
|
||||
|
||||
But as you passed the `HTMLResponse` in the `response_class`, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
|
||||
But as you passed the `HTMLResponse` in the `response_class` too, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Available responses
|
||||
|
||||
Here are some of the available responses.
|
||||
|
||||
Have in mind that you can use `Response` to return anything else, or even create a custom sub-class.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||
|
||||
### `Response`
|
||||
|
||||
The main `Response` class, all the other responses inherit from it.
|
||||
|
||||
You can return it directly.
|
||||
|
||||
It accepts the following parameters:
|
||||
|
||||
* `content` - A `str` or `bytes`.
|
||||
* `status_code` - An `int` HTTP status code.
|
||||
* `headers` - A `dict` of strings.
|
||||
* `media_type` - A `str` giving the media type. E.g. `"text/html"`.
|
||||
|
||||
FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the media_type and appending a charset for text types.
|
||||
|
||||
```Python hl_lines="1 18"
|
||||
{!./src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
### `HTMLResponse`
|
||||
|
||||
Takes some text or bytes and returns an HTML response, as you read above.
|
||||
|
||||
### `PlainTextResponse`
|
||||
|
||||
Takes some text or bytes and returns an plain text response.
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!./src/custom_response/tutorial005.py!}
|
||||
```
|
||||
|
||||
### `JSONResponse`
|
||||
|
||||
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 <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.
|
||||
|
||||
```Python hl_lines="2 9"
|
||||
{!./src/custom_response/tutorial006.py!}
|
||||
```
|
||||
|
||||
### `StreamingResponse`
|
||||
|
||||
Takes an async generator or a normal generator/iterator and streams the response body.
|
||||
|
||||
```Python hl_lines="2 14"
|
||||
{!./src/custom_response/tutorial007.py!}
|
||||
```
|
||||
|
||||
#### Using `StreamingResponse` with file-like objects
|
||||
|
||||
If you have a file-like object (e.g. the object returned by `open()`), you can return it in a `StreamingResponse`.
|
||||
|
||||
This includes many libraries to interact with cloud storage, video processing, and others.
|
||||
|
||||
```Python hl_lines="2 10 11"
|
||||
{!./src/custom_response/tutorial008.py!}
|
||||
```
|
||||
|
||||
!!! 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`.
|
||||
|
||||
### `FileResponse`
|
||||
|
||||
Asynchronously streams a file as the response.
|
||||
|
||||
Takes a different set of arguments to instantiate than the other response types:
|
||||
|
||||
* `path` - The filepath to the file to stream.
|
||||
* `headers` - Any custom headers to include, as a dictionary.
|
||||
* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type.
|
||||
* `filename` - If set, this will be included in the response `Content-Disposition`.
|
||||
|
||||
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
|
||||
|
||||
```Python hl_lines="2 10"
|
||||
{!./src/custom_response/tutorial009.py!}
|
||||
```
|
||||
|
||||
## Additional documentation
|
||||
|
||||
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -161,7 +161,7 @@ pip install aiofiles
|
||||
|
||||
### Serve the static files
|
||||
|
||||
* Import `StaticFiles` from Starlette.
|
||||
* Import `StaticFiles`.
|
||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
```Python hl_lines="7 11"
|
||||
|
||||
97
docs/advanced/middleware.md
Normal file
97
docs/advanced/middleware.md
Normal file
@@ -0,0 +1,97 @@
|
||||
In the main tutorial you read how to add [Custom Middleware](../tutorial/middleware.md){.internal-link target=_blank} to your application.
|
||||
|
||||
And then you also read how to handle [CORS with the `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
|
||||
|
||||
In this section we'll see how to use other middlewares.
|
||||
|
||||
## Adding ASGI middlewares
|
||||
|
||||
As **FastAPI** is based on Starlette and implements the <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr> specification, you can use any ASGI middleware.
|
||||
|
||||
A middleware doesn't have to be made for FastAPI or Starlette to work, as long as it follows the ASGI spec.
|
||||
|
||||
In general, ASGI middlewares are classes that expect to receive an ASGI app as the first argument.
|
||||
|
||||
So, in the documentation for third-party ASGI middlewares they will probably tell you to do something like:
|
||||
|
||||
```Python
|
||||
from unicorn import UnicornMiddleware
|
||||
|
||||
app = SomeASGIApp()
|
||||
|
||||
new_app = UnicornMiddleware(app, some_config="rainbow")
|
||||
```
|
||||
|
||||
But FastAPI (actually Starlette) provides a simpler way to do it that makes sure that the internal middlewares to handle server errors and custom exception handlers work properly.
|
||||
|
||||
For that, you use `app.add_middleware()` (as in the example for CORS).
|
||||
|
||||
```Python
|
||||
from fastapi import FastAPI
|
||||
from unicorn import UnicornMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
```
|
||||
|
||||
`app.add_middleware()` receives a middleware class as the first argument and any additional arguments to be passed to the middleware.
|
||||
|
||||
## Integrated middlewares
|
||||
|
||||
**FastAPI** includes several middlewares for common use cases, we'll see next how to use them.
|
||||
|
||||
!!! note "Technical Details"
|
||||
For the next examples, you could also use `from starlette.middleware.something import SomethingMiddleware`.
|
||||
|
||||
**FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette.
|
||||
|
||||
## `HTTPSRedirectMiddleware`
|
||||
|
||||
Enforces that all incoming requests must either be `https` or `wss`.
|
||||
|
||||
Any incoming requests to `http` or `ws` will be redirected to the secure scheme instead.
|
||||
|
||||
```Python hl_lines="2 6"
|
||||
{!./src/advanced_middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
## `TrustedHostMiddleware`
|
||||
|
||||
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
|
||||
|
||||
```Python hl_lines="2 6 7 8"
|
||||
{!./src/advanced_middleware/tutorial002.py!}
|
||||
```
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains to allow any hostname either use `allowed_hosts=["*"]` or omit the middleware.
|
||||
|
||||
If an incoming request does not validate correctly then a `400` response will be sent.
|
||||
|
||||
## `GZipMiddleware`
|
||||
|
||||
Handles GZip responses for any request that includes `"gzip"` in the `Accept-Encoding` header.
|
||||
|
||||
The middleware will handle both standard and streaming responses.
|
||||
|
||||
```Python hl_lines="2 6 7 8"
|
||||
{!./src/advanced_middleware/tutorial002.py!}
|
||||
```
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
|
||||
|
||||
## Other middlewares
|
||||
|
||||
There are many other ASGI middlewares.
|
||||
|
||||
For example:
|
||||
|
||||
* <a href="https://docs.sentry.io/platforms/python/asgi/" class="external-link" target="_blank">Sentry</a>
|
||||
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn's `ProxyHeadersMiddleware`</a>
|
||||
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
|
||||
|
||||
To see other available middlewares check <a href="https://www.starlette.io/middleware/" class="external-link" target="_blank">Starlette's Middleware docs</a> and the <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">ASGI Awesome List</a>.
|
||||
@@ -18,7 +18,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
||||
|
||||
And then you can set the `status_code` in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="2 11 14"
|
||||
```Python hl_lines="1 9 12"
|
||||
{!./src/response_change_status_code/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
You can declare a parameter of type `Response` in your *path operation function*.
|
||||
|
||||
And then you can set headers in that *temporal* response object.
|
||||
And then you can set cookies in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="2 8 9"
|
||||
```Python hl_lines="1 8 9"
|
||||
{!./src/response_cookies/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -37,4 +37,11 @@ Then set Cookies in it, and then return it:
|
||||
|
||||
### More info
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||
|
||||
And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`.
|
||||
|
||||
To see all the available parameters and options, check the <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">documentation in Starlette</a>.
|
||||
|
||||
@@ -2,20 +2,20 @@ When you create a **FastAPI** *path operation* you can normally return any data
|
||||
|
||||
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}.
|
||||
|
||||
Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a Starlette `JSONResponse` that would be used to send the response to the client.
|
||||
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.
|
||||
|
||||
But you can return a `JSONResponse` directly from your *path operations*.
|
||||
|
||||
It might be useful, for example, to return custom headers or cookies.
|
||||
|
||||
## Starlette `Response`
|
||||
## Return a `Response`
|
||||
|
||||
In fact, you can return any <a href="https://www.starlette.io/responses/" class="external-link" target="_blank">Starlette `Response`</a> or any sub-class of it.
|
||||
In fact, you can return any `Response` or any sub-class of it.
|
||||
|
||||
!!! tip
|
||||
`JSONResponse` itself is a sub-class of `Response`.
|
||||
|
||||
And when you return a Starlette `Response`, **FastAPI** will pass it directly.
|
||||
And when you return a `Response`, **FastAPI** will pass it directly.
|
||||
|
||||
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
|
||||
|
||||
@@ -33,8 +33,10 @@ For those cases, you can use the `jsonable_encoder` to convert your data before
|
||||
{!./src/response_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||
|
||||
## Returning a custom `Response`
|
||||
|
||||
@@ -42,13 +44,11 @@ The example above shows all the parts you need, but it's not very useful yet, as
|
||||
|
||||
Now, let's see how you could use that to return a custom response.
|
||||
|
||||
Let's say you want to return a response that is not available in the default <a href="https://www.starlette.io/responses/" class="external-link" target="_blank">Starlette `Response`s</a>.
|
||||
Let's say that you want to return an <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> response.
|
||||
|
||||
Let's say that you want to return <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>.
|
||||
You could put your XML content in a string, put it in a `Response`, and return it:
|
||||
|
||||
You could put your XML content in a string, put it in a Starlette Response, and return it:
|
||||
|
||||
```Python hl_lines="2 20"
|
||||
```Python hl_lines="1 18"
|
||||
{!./src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
||||
|
||||
And then you can set headers in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="2 8 9"
|
||||
```Python hl_lines="1 7 8"
|
||||
{!./src/response_headers/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -26,6 +26,13 @@ Create a response as described in [Return a Response Directly](response-directly
|
||||
{!./src/response_headers/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||
|
||||
And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`.
|
||||
|
||||
## Custom Headers
|
||||
|
||||
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">using the 'X-' prefix</a>.
|
||||
|
||||
@@ -34,7 +34,7 @@ Use a dependency to check if the username and password are correct.
|
||||
|
||||
For this, use the Python standard module <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> to check the username and password:
|
||||
|
||||
```Python hl_lines="1 13 14 15"
|
||||
```Python hl_lines="1 11 12 13"
|
||||
{!./src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -100,6 +100,6 @@ That way, using `secrets.compare_digest()` in your application code, it will be
|
||||
|
||||
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
|
||||
|
||||
```Python hl_lines="16 17 18 19 20"
|
||||
```Python hl_lines="15 16 17 18 19"
|
||||
{!./src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -54,7 +54,7 @@ They are normally used to declare specific security permissions, for example:
|
||||
|
||||
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes:
|
||||
|
||||
```Python hl_lines="2 5 9 13 48 66 107 109 110 111 112 113 114 115 116 117 123 124 125 126 130 131 132 133 134 135 136 141 155"
|
||||
```Python hl_lines="2 5 9 13 47 65 106 108 109 110 111 112 113 114 115 116 122 123 124 125 129 130 131 132 133 134 135 140 154"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -66,7 +66,7 @@ The first change is that now we are declaring the OAuth2 security scheme with tw
|
||||
|
||||
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
|
||||
|
||||
```Python hl_lines="64 65 66 67"
|
||||
```Python hl_lines="63 64 65 66"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -91,7 +91,7 @@ And we return the scopes as part of the JWT token.
|
||||
|
||||
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
|
||||
|
||||
```Python hl_lines="156"
|
||||
```Python hl_lines="155"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -116,7 +116,7 @@ In this case, it requires the scope `me` (it could require more than one scope).
|
||||
|
||||
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
|
||||
|
||||
```Python hl_lines="5 141 168"
|
||||
```Python hl_lines="5 140 167"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -141,7 +141,7 @@ We also declare a special parameter of type `SecurityScopes`, imported from `fas
|
||||
|
||||
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
|
||||
|
||||
```Python hl_lines="9 107"
|
||||
```Python hl_lines="9 106"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -157,7 +157,7 @@ We create an `HTTPException` that we can re-use (`raise`) later at several point
|
||||
|
||||
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in in the `WWW-Authenticate` header (this is part of the spec).
|
||||
|
||||
```Python hl_lines="107 109 110 111 112 113 114 115 116 117"
|
||||
```Python hl_lines="106 108 109 110 111 112 113 114 115 116"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -175,7 +175,7 @@ Instead of, for example, a `dict`, or something else, as it could break the appl
|
||||
|
||||
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
|
||||
|
||||
```Python hl_lines="48 118 119 120 121 122 123 124 125 126 127 128 129"
|
||||
```Python hl_lines="47 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -185,7 +185,7 @@ We now verify that all the scopes required, by this dependency and all the depen
|
||||
|
||||
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
|
||||
|
||||
```Python hl_lines="130 131 132 133 134 135 136"
|
||||
```Python hl_lines="129 130 131 132 133 134 135"
|
||||
{!./src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ And now in the file `sql_app/main.py` let's integrate and use all the other part
|
||||
|
||||
In a very simplistic way create the database tables:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
```Python hl_lines="9 10 11"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -265,7 +265,7 @@ In a very simplistic way create the database tables:
|
||||
|
||||
Create a dependency that will connect the database right at the beginning of a request and disconnect it at the end:
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25"
|
||||
```Python hl_lines="23 24 25 26 27 28 29"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -273,58 +273,52 @@ Here we have an empty `yield` because we are actually not using the database obj
|
||||
|
||||
It is connecting to the database and storing the connection data in an internal variable that is independent for each request (using the `contextvars` tricks from above).
|
||||
|
||||
Because the database connection is potentially I/O blocking, this dependency is created with a normal `def` function.
|
||||
|
||||
And then, in each *path operation function* that needs to access the database we add it as a dependency.
|
||||
|
||||
But we are not using the value given by this dependency (it actually doesn't give any value, as it has an empty `yield`). So, we don't add it to the *path operation function* but to the *path operation decorator* in the `dependencies` parameter:
|
||||
|
||||
```Python hl_lines="36 44 51 63 69 76"
|
||||
```Python hl_lines="32 40 47 59 65 72"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
### Context Variable Middleware
|
||||
### Context variable sub-dependency
|
||||
|
||||
For all the `contextvars` parts to work, we need to make sure there's a new "context" each time there's a new request, so that we have a specific context variable Peewee can use to save its state (database connection, transactions, etc).
|
||||
For all the `contextvars` parts to work, we need to make sure we have an independent value in the `ContextVar` for each request that uses the database, and that value will be used as the database state (connection, transactions, etc) for the whole request.
|
||||
|
||||
For that, we need to create a middleware.
|
||||
For that, we need to create another `async` dependency `reset_db_state()` that is used as a sub-dependency in `get_db()`. It will set the value for the context variable (with just a default `dict`) that will be used as the database state for the whole request. And then the dependency `get_db()` will store in it the database state (connection, transactions, etc).
|
||||
|
||||
Right before the request, we are going to reset the database state. We will "set" a value to the context variable and then we will ask the Peewee database state to "reset" (this will create the default values it uses).
|
||||
|
||||
And then the rest of the request is processed with that new context variable we just set, all automatically and more or less "magically".
|
||||
|
||||
For the **next request**, as we will reset that context variable again in the middleware, that new request will have its own database state (connection, transactions, etc).
|
||||
|
||||
```Python hl_lines="28 29 30 31 32 33"
|
||||
```Python hl_lines="18 19 20"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
For the **next request**, as we will reset that context variable again in the `async` dependency `reset_db_state()` and then create a new connection in the `get_db()` dependency, that new request will have its own database state (connection, transactions, etc).
|
||||
|
||||
!!! tip
|
||||
As FastAPI is an async framework, one request could start being processed, and before finishing, another request could be received and start processing as well, and it all could be processed in the same thread.
|
||||
|
||||
But context variables are aware of these async features, so, a Peewee database state set in the middleware will keep its own data throughout the entire request.
|
||||
But context variables are aware of these async features, so, a Peewee database state set in the `async` dependency `reset_db_state()` will keep its own data throughout the entire request.
|
||||
|
||||
And at the same time, the other concurrent request will have its own database state that will be independent for the whole request.
|
||||
|
||||
#### Peewee Proxy
|
||||
|
||||
|
||||
If you are using a <a href="http://docs.peewee-orm.com/en/latest/peewee/database.html#dynamically-defining-a-database" class="external-link" target="_blank">Peewee Proxy</a>, the actual database is at `db.obj`.
|
||||
|
||||
So, you would reset it with:
|
||||
|
||||
```Python hl_lines="3 4"
|
||||
@app.middleware("http")
|
||||
async def reset_db_middleware(request: Request, call_next):
|
||||
async def reset_db_state():
|
||||
database.db.obj._state._state.set(db_state_default.copy())
|
||||
database.db.obj._state.reset()
|
||||
response = await call_next(request)
|
||||
return response
|
||||
```
|
||||
|
||||
### Create your **FastAPI** *path operations*
|
||||
|
||||
Now, finally, here's the standard **FastAPI** *path operations* code.
|
||||
|
||||
```Python hl_lines="36 37 38 39 40 41 44 45 46 47 50 51 52 53 54 55 56 57 60 61 62 63 64 65 66 69 70 71 72 75 76 77 78 79 80 81 82 83"
|
||||
```Python hl_lines="32 33 34 35 36 37 40 41 42 43 46 47 48 49 50 51 52 53 56 57 58 59 60 61 62 65 66 67 68 71 72 73 74 75 76 77 78 79"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -364,15 +358,13 @@ If you want to check how Peewee would break your app if used without modificatio
|
||||
# db._state = PeeweeConnectionState()
|
||||
```
|
||||
|
||||
And in the file `sql_app/main.py` file, comment the middleware:
|
||||
And in the file `sql_app/main.py` file, comment the body of the `async` dependency `reset_db_state()` and replace it with a `pass`:
|
||||
|
||||
```Python
|
||||
# @app.middleware("http")
|
||||
# async def reset_db_middleware(request: Request, call_next):
|
||||
async def reset_db_state():
|
||||
# database.db._state._state.set(db_state_default.copy())
|
||||
# database.db._state.reset()
|
||||
# response = await call_next(request)
|
||||
# return response
|
||||
pass
|
||||
```
|
||||
|
||||
Then run your app with Uvicorn:
|
||||
@@ -391,11 +383,11 @@ The tabs will wait for a bit and then some of them will show `Internal Server Er
|
||||
|
||||
### What happens
|
||||
|
||||
The first tab will make your app create a connection to the database and wait for some seconds before replying back and closing the connection.
|
||||
The first tab will make your app create a connection to the database and wait for some seconds before replying back and closing the database connection.
|
||||
|
||||
Then, for the request in the next tab, your app will wait for one second less, and so on.
|
||||
|
||||
This means that it will end up finishing some of the last tabs' requests than some of the previous ones.
|
||||
This means that it will end up finishing some of the last tabs' requests earlier than some of the previous ones.
|
||||
|
||||
Then one the last requests that wait less seconds will try to open a database connection, but as one of those previous requests for the other tabs will probably be handled in the same thread as the first one, it will have the same database connection that is already open, and Peewee will throw an error and you will see it in the terminal, and the response will have an `Internal Server Error`.
|
||||
|
||||
@@ -413,15 +405,12 @@ Now go back to the file `sql_app/database.py`, and uncomment the line:
|
||||
db._state = PeeweeConnectionState()
|
||||
```
|
||||
|
||||
And in the file `sql_app/main.py` file, uncomment the middleware:
|
||||
And in the file `sql_app/main.py` file, uncomment the body of the `async` dependency `reset_db_state()`:
|
||||
|
||||
```Python
|
||||
@app.middleware("http")
|
||||
async def reset_db_middleware(request: Request, call_next):
|
||||
async def reset_db_state():
|
||||
database.db._state._state.set(db_state_default.copy())
|
||||
database.db._state.reset()
|
||||
response = await call_next(request)
|
||||
return response
|
||||
```
|
||||
|
||||
Terminate your running app and start it again.
|
||||
@@ -440,31 +429,31 @@ Repeat the same process with the 10 tabs. This time all of them will wait and yo
|
||||
|
||||
* `sql_app/database.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/database.py!}
|
||||
```
|
||||
|
||||
* `sql_app/models.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/models.py!}
|
||||
```
|
||||
|
||||
* `sql_app/schemas.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
* `sql_app/crud.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/crud.py!}
|
||||
```
|
||||
|
||||
* `sql_app/main.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -477,11 +466,11 @@ Repeat the same process with the 10 tabs. This time all of them will wait and yo
|
||||
|
||||
Peewee uses <a href="https://docs.python.org/3/library/threading.html#thread-local-data" class="external-link" target="_blank">`threading.local`</a> by default to store it's database "state" data (connection, transactions, etc).
|
||||
|
||||
`threading.local` creates a value exclusive to the current thread, but an async framework would run all the "tasks" (e.g. requests) in the same thread, and possibly not in order.
|
||||
`threading.local` creates a value exclusive to the current thread, but an async framework would run all the code (e.g. for each request) in the same thread, and possibly not in order.
|
||||
|
||||
On top of that, an async framework could run some sync code in a threadpool (using `asyncio.run_in_executor`), but belonging to the same "task" (e.g. to the same request).
|
||||
On top of that, an async framework could run some sync code in a threadpool (using `asyncio.run_in_executor`), but belonging to the same request.
|
||||
|
||||
This means that, with Peewee's current implementation, multiple tasks could be using the same `threading.local` variable and end up sharing the same connection and data, and at the same time, if they execute sync IO-blocking code in a threadpool (as with normal `def` functions in FastAPI, in *path operations* and dependencies), that code won't have access to the database state variables, even while it's part of the same "task" (request) and it should be able to get access to that.
|
||||
This means that, with Peewee's current implementation, multiple tasks could be using the same `threading.local` variable and end up sharing the same connection and data (that they shouldn't), and at the same time, if they execute sync I/O-blocking code in a threadpool (as with normal `def` functions in FastAPI, in *path operations* and dependencies), that code won't have access to the database state variables, even while it's part of the same request and it should be able to get access to the same database state.
|
||||
|
||||
### Context variables
|
||||
|
||||
@@ -489,46 +478,44 @@ Python 3.7 has <a href="https://docs.python.org/3/library/contextvars.html" clas
|
||||
|
||||
There are several things to have in mind.
|
||||
|
||||
The `ContextVar` has to be created at the top of the module, like `some_var = ContextVar("some_var", default="default value")`.
|
||||
The `ContextVar` has to be created at the top of the module, like:
|
||||
|
||||
To set a value used in the current "context" (e.g. for the current request) use `some_var.set("new value")`.
|
||||
```Python
|
||||
some_var = ContextVar("some_var", default="default value")
|
||||
```
|
||||
|
||||
To get a value anywhere inside of the context (e.g. in any part handling the current request) use `some_var.get()`.
|
||||
To set a value used in the current "context" (e.g. for the current request) use:
|
||||
|
||||
### Set context variables in middleware
|
||||
```Python
|
||||
some_var.set("new value")
|
||||
```
|
||||
|
||||
If some part of the async code sets the value with `some_var.set("updated in function")` (e.g. the middleware), the rest of the code in it will see that new value.
|
||||
To get a value anywhere inside of the context (e.g. in any part handling the current request) use:
|
||||
|
||||
And if it calls any other function with `await some_function()` (e.g. `response = await call_next(request)` in our middleware) that internal `some_function()` (or `response = await call_next(request)` in our example) and everything it calls inside, will see that same new value `"updated in function"`.
|
||||
```Python
|
||||
some_var.get()
|
||||
```
|
||||
|
||||
So, in our case, if we set the Peewee state variable in the middleware and then call `response = await call_next(request)` all the rest of the internal code in our app (that is called by `call_next()`) will see this value we set in the middleware and will be able to reuse it.
|
||||
### Set context variables in the `async` dependency `reset_db_state()`
|
||||
|
||||
But if the value is set in an internal function (e.g. in `get_db()`) that value will be seen only by that internal function and any code it calls, not by the parent function nor by any sibling function. So, we can't set the Peewee database state in `get_db()`, or the *path operation functions* wouldn't see the new Peewee database state for that "context".
|
||||
If some part of the async code sets the value with `some_var.set("updated in function")` (e.g. like the `async` dependency), the rest of the code in it and the code that goes after (including code inside of `async` functions called with `await`) will see that new value.
|
||||
|
||||
### But `get_db` is an async context manager
|
||||
So, in our case, if we set the Peewee state variable (with a default `dict`) in the `async` dependency, all the rest of the internal code in our app will see this value and will be able to reuse it for the whole request.
|
||||
|
||||
You might be thinking that `get_db()` is actually not used as a function, it's converted to a context manager.
|
||||
And the context variable would be set again for the next request, even if they are concurrent.
|
||||
|
||||
So the *path operation function* is part of it.
|
||||
### Set database state in the dependency `get_db()`
|
||||
|
||||
But the code after the `yield`, in the `finally` is not executed in the same "context".
|
||||
As `get_db()` is a normal `def` function, **FastAPI** will make it run in a threadpool, with a *copy* of the "context", holding the same value for the context variable (the `dict` with the reset database state). Then it can add database state to that `dict`, like the connection, etc.
|
||||
|
||||
So, if you reset the state in `get_db()`, the *path operation function* would see the database connection set there. But the `finally` block would not see the same context variable value, and so, as the database object would not have the same context variable for its state, it would not have the same connection, so you couldn't close it in the `finally` in `get_db()` after the request is done.
|
||||
But if the value of the context variable (the default `dict`) was set in that normal `def` function, it would create a new value that would stay only in that thread of the threadpool, and the rest of the code (like the *path operation functions*) wouldn't have access to it. In `get_db()` we can only set values in the `dict`, but not the entire `dict` itself.
|
||||
|
||||
In the middleware we are setting the Peewee state to a context variable that holds a `dict`. So, it's set for every new request.
|
||||
So, we need to have the `async` dependency `reset_db_state()` to set the `dict` in the context variable. That way, all the code has access to the same `dict` for the database state for a single request.
|
||||
|
||||
And as the database state variables are stored inside of that `dict` instead of new context variables, when Peewee sets the new database state (connection, transactions, etc) in any part of the internal code, underneath, all that will be set as keys in that `dict`. But the `dict` would still be the same we set in the middleware. That's what allows the `get_db()` dependency to make Peewee create a new connection (that is stored in that `dict`) and allows the `finally` block to still have access to the same connection.
|
||||
### Connect and disconnect in the dependency `get_db()`
|
||||
|
||||
Because the context variable is set outside all that, in the middleware.
|
||||
Then the next question would be, why not just connect and disconnect the database in the `async` dependency itself, instead of in `get_db()`?
|
||||
|
||||
### Connect and disconnect in dependency
|
||||
The `async` dependency has to be `async` for the context variable to be preserved for the rest of the request, but creating and closing the database connection is potentially blocking, so it could degrade performance if it was there.
|
||||
|
||||
Then the next question would be, why not just connect and disconnect the database in the middleware itself, instead of `get_db()`?
|
||||
|
||||
First, the middleware has to be `async`, and creating and closing the database connection is potentially blocking, so it could degrade performance.
|
||||
|
||||
But more importantly, the middleware returns a `response`, and this `response` is actually an awaitable function that will do all the work in your code, including background tasks.
|
||||
|
||||
If you closed the connection in the middleware right before returning the `response`, some of your code would not have the chance to use the database connection set in the context variable.
|
||||
|
||||
Because some other code will call that `response` with `await response(...)`. And inside of that `await response(...)` is that, for example, background tasks are run. But if the connection was already closed before `response` is awaited, then it won't be able to access it.
|
||||
So we also need the normal `def` dependency `get_db()`.
|
||||
|
||||
@@ -2,7 +2,7 @@ You can use any template engine you want with **FastAPI**.
|
||||
|
||||
A common election is Jinja2, the same one used by Flask and other tools.
|
||||
|
||||
Starlette has utilities to configure it easily that you can use directly in your **FastAPI** application.
|
||||
There are utilities to configure it easily that you can use directly in your **FastAPI** application (provided by Starlette).
|
||||
|
||||
## Install dependencies
|
||||
|
||||
@@ -20,18 +20,23 @@ pip install aiofiles
|
||||
|
||||
## Using `Jinja2Templates`
|
||||
|
||||
* Import `Jinja2Templates` form Starlette.
|
||||
* Import `Jinja2Templates`.
|
||||
* Create a `templates` object that you can re-use later.
|
||||
* Declare a `Request` parameter in the *path operation* that will return a template.
|
||||
* Use the `templates` you created to render and return a `TemplateResponse`, passing the `request` as one of the key-value pairs in the Jinja2 "context".
|
||||
|
||||
```Python hl_lines="4 11 15 16"
|
||||
```Python hl_lines="3 10 14 15"
|
||||
{!./src/templates/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you have to pass the `request` as part of the key-value pairs in the context for Jinja2. So, you also have to declare it in your *path operation*.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.templating import Jinja2Templates`.
|
||||
|
||||
**FastAPI** provides the same `starlette.templating` as `fastapi.templating` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request` and `StaticFiles`.
|
||||
|
||||
## Writing templates
|
||||
|
||||
Then you can write a template at `templates/item.html` with:
|
||||
|
||||
@@ -15,7 +15,7 @@ But there are situations where you might need to access the `Request` object dir
|
||||
|
||||
As **FastAPI** is actually **Starlette** underneath, with a layer of several tools on top, you can use Starlette's <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> object directly when you need to.
|
||||
|
||||
It would also mean that if you get data from the `Request` object directly (for example, read the body) it won't be validated, converted or annotated (with OpenAPI, for the automatic documentation) by FastAPI.
|
||||
It would also mean that if you get data from the `Request` object directly (for example, read the body) it won't be validated, converted or documented (with OpenAPI, for the automatic API user interface) by FastAPI.
|
||||
|
||||
Although any other parameter declared normally (for example, the body with a Pydantic model) would still be validated, converted, annotated, etc.
|
||||
|
||||
@@ -27,24 +27,14 @@ Let's imagine you want to get the client's IP address/host inside of your *path
|
||||
|
||||
For that you need to access the request directly.
|
||||
|
||||
### Import the `Request`
|
||||
|
||||
First, import the `Request` class from Starlette:
|
||||
|
||||
```Python hl_lines="2"
|
||||
```Python hl_lines="1 7 8"
|
||||
{!./src/using_request_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Declare the `Request` parameter
|
||||
|
||||
Then declare a *path operation function* parameter with the type being the `Request` class:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./src/using_request_directly/tutorial001.py!}
|
||||
```
|
||||
By declaring a *path operation function* parameter with the type being the `Request` **FastAPI** will know to pass the `Request` in that parameter.
|
||||
|
||||
!!! tip
|
||||
Note that in this case, we are declaring a path parameter besides the request parameter.
|
||||
Note that in this case, we are declaring a path parameter beside the request parameter.
|
||||
|
||||
So, the path parameter will be extracted, validated, converted to the specified type and annotated with OpenAPI.
|
||||
|
||||
@@ -53,3 +43,8 @@ Then declare a *path operation function* parameter with the type being the `Requ
|
||||
## `Request` documentation
|
||||
|
||||
You can read more details about the <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request` object in the official Starlette documentation site</a>.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.requests import Request`.
|
||||
|
||||
**FastAPI** provides it directly just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||
|
||||
@@ -23,7 +23,7 @@ In production you would have one of the options above.
|
||||
|
||||
But it's the simplest way to focus on the server-side of WebSockets and have a working example:
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 42 43 44"
|
||||
```Python hl_lines="2 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 41 42 43"
|
||||
{!./src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -31,18 +31,20 @@ But it's the simplest way to focus on the server-side of WebSockets and have a w
|
||||
|
||||
In your **FastAPI** application, create a `websocket`:
|
||||
|
||||
```Python hl_lines="3 47 48"
|
||||
```Python hl_lines="1 46 47"
|
||||
{!./src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
In this example we are importing `WebSocket` from `starlette.websockets` to use it in the type declaration in the WebSocket route function.
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.websockets import WebSocket`.
|
||||
|
||||
**FastAPI** provides the same `WebSocket` directly just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||
|
||||
## Await for messages and send messages
|
||||
|
||||
In your WebSocket route you can `await` for messages and send messages.
|
||||
|
||||
```Python hl_lines="49 50 51 52 53"
|
||||
```Python hl_lines="48 49 50 51 52"
|
||||
{!./src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -61,7 +63,7 @@ In WebSocket endpoints you can import from `fastapi` and use:
|
||||
|
||||
They work the same way as for other FastAPI endpoints/*path operations*:
|
||||
|
||||
```Python hl_lines="55 56 57 58 59 60 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78"
|
||||
```Python hl_lines="53 54 55 56 57 58 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76"
|
||||
{!./src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -76,7 +78,6 @@ They work the same way as for other FastAPI endpoints/*path operations*:
|
||||
|
||||
To learn more about the options, check Starlette's documentation for:
|
||||
|
||||
* <a href="https://www.starlette.io/applications/" class="external-link" target="_blank">Applications (`websocket_route`)</a>.
|
||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
|
||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
|
||||
|
||||
|
||||
35
docs/advanced/wsgi.md
Normal file
35
docs/advanced/wsgi.md
Normal file
@@ -0,0 +1,35 @@
|
||||
You can mount WSGI applications as you saw with [Sub Applications - Behind a Proxy, Mounts](./sub-applications-proxy.md){.internal-link target=_blank}.
|
||||
|
||||
For that, you can use the `WSGIMiddleware` and use it to wrap your WSGI application, for example, Flask, Django, etc.
|
||||
|
||||
## Using `WSGIMiddleware`
|
||||
|
||||
You need to import `WSGIMiddleware`.
|
||||
|
||||
Then wrap the WSGI (e.g. Flask) app with the middleware.
|
||||
|
||||
And then mount that under a path.
|
||||
|
||||
```Python hl_lines="1 3 22"
|
||||
{!./src/wsgi/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Check it
|
||||
|
||||
Now, every request under the path `/v1/` will be handled by the Flask application.
|
||||
|
||||
And the rest will be handled by **FastAPI**.
|
||||
|
||||
If you run it with Uvicorn and go to <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a> you will see the response from Flask:
|
||||
|
||||
```txt
|
||||
Hello, World from Flask!
|
||||
```
|
||||
|
||||
And if you go to <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> you will see the response from FastAPI:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World"
|
||||
}
|
||||
```
|
||||
@@ -229,7 +229,7 @@ It was one of the first extremely fast Python frameworks based on `asyncio`. It
|
||||
!!! note "Technical Details"
|
||||
It used <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> instead of the default Python `asyncio` loop. That's what made it so fast.
|
||||
|
||||
It <a href="https://github.com/huge-success/sanic/issues/761" class="external-link" target="_blank">still doesn't implement the ASGI spec for Python asynchronous web development</a>, but it clearly inspired Uvicorn and Starlette, that are currently faster than Sanic in open benchmarks.
|
||||
It clearly inspired Uvicorn and Starlette, that are currently faster than Sanic in open benchmarks.
|
||||
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
Find a way to have a crazy performance.
|
||||
|
||||
@@ -144,7 +144,7 @@ You go with your crush to get parallel fast food.
|
||||
|
||||
You stand in line while several (let's say 8) cashiers take the orders from the people in front of you.
|
||||
|
||||
Everyone before you is waiting for their burgers to be ready before leaving the counter because each of the 8 cashiers goes himself and preparers the burger right away before getting the next order.
|
||||
Everyone before you is waiting for their burgers to be ready before leaving the counter because each of the 8 cashiers goes himself and prepares the burger right away before getting the next order.
|
||||
|
||||
Then it's finally your turn, you place your order of 2 very fancy burgers for your crush and you.
|
||||
|
||||
|
||||
@@ -1,4 +1,98 @@
|
||||
You can use <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
|
||||
Deploying a **FastAPI** application is relatively easy.
|
||||
|
||||
There are several ways to do it depending on your specific use case and the tools that you use.
|
||||
|
||||
You will see more about some of the ways to do it in the next sections.
|
||||
|
||||
## FastAPI versions
|
||||
|
||||
**FastAPI** is already being used in production in many applications and systems. And the test coverage is kept at 100%. But its development is still moving quickly.
|
||||
|
||||
New features are added frequently, bugs are fixed regularly, and the code is still continuously improving.
|
||||
|
||||
That's why the current versions are still `0.x.x`, this reflects that each version could potentially have breaking changes. This follows the <a href="https://semver.org/" class="external-link" target="_blank">Semantic Versioning</a> conventions.
|
||||
|
||||
You can create production applications with **FastAPI** right now (and you have probably been doing it for some time), you just have to make sure that you use a version that works correctly with the rest of your code.
|
||||
|
||||
### Pin your `fastapi` version
|
||||
|
||||
The first thing you should do is to "pin" the version of **FastAPI** you are using to the specific latest version that you know works correctly for your application.
|
||||
|
||||
For example, let's say you are using version `0.45.0` in your app.
|
||||
|
||||
If you use a `requirements.txt` file you could specify the version with:
|
||||
|
||||
```txt
|
||||
fastapi==0.45.0
|
||||
```
|
||||
|
||||
that would mean that you would use exactly the version `0.45.0`.
|
||||
|
||||
Or you could also pin it with:
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
that would mean that you would use the versions `0.45.0` or above, but less than `0.46.0`, for example, a version `0.45.2` would still be accepted.
|
||||
|
||||
If you use any other tool to manage your installations, like Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages.
|
||||
|
||||
### Available versions
|
||||
|
||||
You can see the available versions (e.g. to check what is the current latest) in the [Release Notes](release-notes.md){.internal-link target=_blank}.
|
||||
|
||||
### About versions
|
||||
|
||||
Following the Semantic Versioning conventions, any version below `1.0.0` could potentially add breaking changes.
|
||||
|
||||
FastAPI also follows the convention that any "PATCH" version change is for bug fixes and non-breaking changes.
|
||||
|
||||
!!! tip
|
||||
The "PATCH" is the last number, for example, in `0.2.3`, the PATCH version is `3`.
|
||||
|
||||
So, you should be able to pin to a version like:
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
Breaking changes and new features are added in "MINOR" versions.
|
||||
|
||||
!!! tip
|
||||
The "MINOR" is the number in the middle, for example, in `0.2.3`, the MINOR version is `2`.
|
||||
|
||||
### Upgrading the FastAPI versions
|
||||
|
||||
You should add tests for your app.
|
||||
|
||||
With **FastAPI** it's very easy (thanks to Starlette), check the docs: [Testing](tutorial/testing.md){.internal-link target=_blank}
|
||||
|
||||
After you have tests, then you can upgrade the **FastAPI** version to a more recent one, and make sure that all your code is working correctly by running your tests.
|
||||
|
||||
If everything is working, or after you make the necessary changes, and all your tests are passing, then you can pin your `fastapi` to that new recent version.
|
||||
|
||||
### About Starlette
|
||||
|
||||
You shouldn't pin the version of `starlette`.
|
||||
|
||||
Different versions of **FastAPI** will use a specific newer version of Starlette.
|
||||
|
||||
So, you can just let **FastAPI** use the correct Starlette version.
|
||||
|
||||
### About Pydantic
|
||||
|
||||
Pydantic includes the tests for **FastAPI** with its own tests, so new versions of Pydantic (above `1.0.0`) are always compatible with FastAPI.
|
||||
|
||||
You can pin Pydantic to any version above `1.0.0` that works for you and below `2.0.0`.
|
||||
|
||||
For example:
|
||||
|
||||
```txt
|
||||
pydantic>=1.2.0,<2.0.0
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
In this section you'll see instructions and links to guides to know how to:
|
||||
|
||||
@@ -7,11 +101,7 @@ In this section you'll see instructions and links to guides to know how to:
|
||||
* Set up a Docker Swarm mode cluster with automatic HTTPS, even on a simple $5 USD/month server. In about **20 min**.
|
||||
* Generate and deploy a full **FastAPI** application, using your Docker Swarm cluster, with HTTPS, etc. In about **10 min**.
|
||||
|
||||
---
|
||||
|
||||
You can also easily use **FastAPI** in a standard server directly too (without Docker).
|
||||
|
||||
## Docker
|
||||
You can use <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
|
||||
|
||||
If you are using Docker, you can use the official Docker image:
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ Here's an incomplete list of some of them.
|
||||
|
||||
* <a href="https://medium.com/@arthur393/another-boilerplate-to-fastapi-azure-pipeline-ci-pytest-3c8d9a4be0bb" class="external-link" target="_blank">Another Boilerplate to FastAPI: Azure Pipeline CI + Pytest</a> by <a href="https://twitter.com/arthurheinrique" class="external-link" target="_blank">Arthur Henrique</a>.
|
||||
|
||||
* <a href="https://iwpnd.pw/articles/2020-01/deploy-fastapi-to-aws-lambda" class="external-link" target="_blank">How to continuously deploy a FastAPI to AWS Lambda with AWS SAM</a> by <a href="https://iwpnd.pw" class="external-link" target="_blank">Benjamin Ramser</a>.
|
||||
|
||||
* <a href="https://www.tutlinks.com/create-and-deploy-fastapi-app-to-heroku/" class="external-link" target="_blank">Create and Deploy FastAPI app to Heroku without using Docker</a> by <a href="https://www.linkedin.com/in/navule/" class="external-link" target="_blank">Navule Pavan Kumar Rao</a>.
|
||||
|
||||
### Japanese
|
||||
|
||||
* <a href="https://qiita.com/mtitg/items/47770e9a562dd150631d" class="external-link" target="_blank">FastAPI|DB接続してCRUDするPython製APIサーバーを構築</a> by <a href="https://qiita.com/mtitg" class="external-link" target="_blank">@mtitg</a>.
|
||||
@@ -75,6 +79,10 @@ Here's an incomplete list of some of them.
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-admin-page-improvement" class="external-link" target="_blank">【第4回】FastAPIチュートリアル: toDoアプリを作ってみよう【管理者ページ改良編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://qiita.com/bee2/items/0ad260ab9835a2087dae" class="external-link" target="_blank">PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
|
||||
|
||||
* <a href="https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9" class="external-link" target="_blank">[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
|
||||
|
||||
### Chinese
|
||||
|
||||
* <a href="https://cloud.tencent.com/developer/article/1431448" class="external-link" target="_blank">使用FastAPI框架快速构建高性能的api服务</a> by <a href="https://cloud.tencent.com/developer/user/5471722" class="external-link" target="_blank">逍遥散人</a>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Are you liking **FastAPI**?
|
||||
Do you like **FastAPI**?
|
||||
|
||||
Would you like to help FastAPI, other users, and the author?
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 30 KiB |
@@ -319,7 +319,7 @@ Coming back to the previous code example, **FastAPI** will:
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that is has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
* All this would also work for deeply nested JSON objects.
|
||||
* Convert from and to JSON automatically.
|
||||
@@ -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]`.
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ Taken from the official Pydantic docs:
|
||||
|
||||
**FastAPI** is all based on Pydantic.
|
||||
|
||||
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/){.internal-link target=_blank}.
|
||||
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Type hints in **FastAPI**
|
||||
|
||||
@@ -276,7 +276,7 @@ With **FastAPI** you declare parameters with type hints and you get:
|
||||
* **Document** the API using OpenAPI:
|
||||
* which is then used by the automatic interactive documentation user interfaces.
|
||||
|
||||
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/){.internal-link target=_blank}.
|
||||
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc), **FastAPI** will do a lot of the work for you.
|
||||
|
||||
|
||||
@@ -1,5 +1,81 @@
|
||||
## Latest changes
|
||||
|
||||
## 0.52.0
|
||||
|
||||
* Add new high-performance JSON response class using `orjson`. New docs: [Custom Response - HTML, Stream, File, others: `ORJSONResponse`](https://fastapi.tiangolo.com/advanced/custom-response/#use-orjsonresponse). PR [#1065](https://github.com/tiangolo/fastapi/pull/1065).
|
||||
|
||||
## 0.51.0
|
||||
|
||||
* Re-export utils from Starlette:
|
||||
* This allows using things like `from fastapi.responses import JSONResponse` instead of `from starlette.responses import JSONResponse`.
|
||||
* It's mainly syntax sugar, a convenience for developer experience.
|
||||
* Now `Request`, `Response`, `WebSocket`, `status` can be imported directly from `fastapi` as in `from fastapi import Response`. This is because those are frequently used, to use the request directly, to set headers and cookies, to get status codes, etc.
|
||||
* Documentation changes in many places, but new docs and noticeable improvements:
|
||||
* [Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
|
||||
* [Advanced Middleware](https://fastapi.tiangolo.com/advanced/middleware/).
|
||||
* [Including WSGI - Flask, Django, others](https://fastapi.tiangolo.com/advanced/wsgi/).
|
||||
* PR [#1064](https://github.com/tiangolo/fastapi/pull/1064).
|
||||
|
||||
## 0.50.0
|
||||
|
||||
* Add link to Release Notes from docs about pinning versions for deployment. PR [#1058](https://github.com/tiangolo/fastapi/pull/1058).
|
||||
* Upgrade code to use the latest version of Starlette, including:
|
||||
* Several bug fixes.
|
||||
* Optional redirects of slashes, with or without ending in `/`.
|
||||
* Events for routers, `"startup"`, and `"shutdown"`.
|
||||
* PR [#1057](https://github.com/tiangolo/fastapi/pull/1057).
|
||||
* Add docs about pinning FastAPI versions for deployment: [Deployment: FastAPI versions](https://fastapi.tiangolo.com/deployment/#fastapi-versions). PR [#1056](https://github.com/tiangolo/fastapi/pull/1056).
|
||||
|
||||
## 0.49.2
|
||||
|
||||
* Fix links in release notes. PR [#1052](https://github.com/tiangolo/fastapi/pull/1052) by [@sattosan](https://github.com/sattosan).
|
||||
* Fix typo in release notes. PR [#1051](https://github.com/tiangolo/fastapi/pull/1051) by [@sattosan](https://github.com/sattosan).
|
||||
* Refactor/clarify `serialize_response` parameter name to avoid confusion. PR [#1031](https://github.com/tiangolo/fastapi/pull/1031) by [@patrickmckenna](https://github.com/patrickmckenna).
|
||||
* Refactor calling each a path operation's handler function in an isolated function, to simplify profiling. PR [#1027](https://github.com/tiangolo/fastapi/pull/1027) by [@sm-Fifteen](https://github.com/sm-Fifteen).
|
||||
* Add missing dependencies for testing. PR [#1026](https://github.com/tiangolo/fastapi/pull/1026) by [@sm-Fifteen](https://github.com/sm-Fifteen).
|
||||
* Fix accepting valid types for response models, including Python types like `List[int]`. PR [#1017](https://github.com/tiangolo/fastapi/pull/1017) by [@patrickmckenna](https://github.com/patrickmckenna).
|
||||
* Fix format in SQL tutorial. PR [#1015](https://github.com/tiangolo/fastapi/pull/1015) by [@vegarsti](https://github.com/vegarsti).
|
||||
|
||||
## 0.49.1
|
||||
|
||||
* Fix path operation duplicated parameters when used in dependencies and the path operation function. PR [#994](https://github.com/tiangolo/fastapi/pull/994) by [@merowinger92](https://github.com/merowinger92).
|
||||
* Update Netlify previews deployment GitHub action as the fix is already merged and there's a new release. PR [#1047](https://github.com/tiangolo/fastapi/pull/1047).
|
||||
* Move mypy configurations to config file. PR [#987](https://github.com/tiangolo/fastapi/pull/987) by [@hukkinj1](https://github.com/hukkinj1).
|
||||
* Temporary fix to Netlify previews not deployable from PRs from forks. PR [#1046](https://github.com/tiangolo/fastapi/pull/1046) by [@mariacamilagl](https://github.com/mariacamilagl).
|
||||
|
||||
## 0.49.0
|
||||
|
||||
* Fix encoding of `pathlib` paths in `jsonable_encoder`. PR [#978](https://github.com/tiangolo/fastapi/pull/978) by [@patrickmckenna](https://github.com/patrickmckenna).
|
||||
* Add articles to [External Links](https://fastapi.tiangolo.com/external-links/): [PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)](https://qiita.com/bee2/items/0ad260ab9835a2087dae) and [[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する](https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9). PR [#974](https://github.com/tiangolo/fastapi/pull/974) by [@tokusumi](https://github.com/tokusumi).
|
||||
* Fix broken links in docs. PR [#949](https://github.com/tiangolo/fastapi/pull/949) by [@tsotnikov](https://github.com/tsotnikov).
|
||||
* Fix small typos. PR [#941](https://github.com/tiangolo/fastapi/pull/941) by [@NikitaKolesov](https://github.com/NikitaKolesov).
|
||||
* Update and clarify docs for dependencies with `yield`. PR [#986](https://github.com/tiangolo/fastapi/pull/986).
|
||||
* Add Mermaid JS support for diagrams in docs. Add first diagrams to [Dependencies: First Steps](https://fastapi.tiangolo.com/tutorial/dependencies/) and [Dependencies with `yield` and `HTTPException`](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-httpexception). PR [#985](https://github.com/tiangolo/fastapi/pull/985).
|
||||
* Update CI to run docs deployment in GitHub actions. PR [#983](https://github.com/tiangolo/fastapi/pull/983).
|
||||
* Allow `callable`s in *path operation functions*, like functions modified with `functools.partial`. PR [#977](https://github.com/tiangolo/fastapi/pull/977).
|
||||
|
||||
## 0.48.0
|
||||
|
||||
* Run linters first in tests to error out faster. PR [#948](https://github.com/tiangolo/fastapi/pull/948).
|
||||
* Log warning about `email-validator` only when used. PR [#946](https://github.com/tiangolo/fastapi/pull/946).
|
||||
* Simplify [Peewee docs](https://fastapi.tiangolo.com/advanced/sql-databases-peewee/) with double dependency with `yield`. PR [#947](https://github.com/tiangolo/fastapi/pull/947).
|
||||
* Add article [External Links](https://fastapi.tiangolo.com/external-links/): [Create and Deploy FastAPI app to Heroku](https://www.tutlinks.com/create-and-deploy-fastapi-app-to-heroku/). PR [#942](https://github.com/tiangolo/fastapi/pull/942) by [@windson](https://github.com/windson).
|
||||
* Update description of Sanic, as it is now ASGI too. PR [#932](https://github.com/tiangolo/fastapi/pull/932) by [@raphaelauv](https://github.com/raphaelauv).
|
||||
* Fix typo in main page. PR [#920](https://github.com/tiangolo/fastapi/pull/920) by [@mMarzeta](https://github.com/mMarzeta).
|
||||
* Fix parsing of possibly invalid bodies. PR [#918](https://github.com/tiangolo/fastapi/pull/918) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Fix typo [#916](https://github.com/tiangolo/fastapi/pull/916) by [@adursun](https://github.com/adursun).
|
||||
* Allow `Any` type for enums in OpenAPI. PR [#906](https://github.com/tiangolo/fastapi/pull/906) by [@songzhi](https://github.com/songzhi).
|
||||
* Add article to [External Links](https://fastapi.tiangolo.com/external-links/): [How to continuously deploy a FastAPI to AWS Lambda with AWS SAM](https://iwpnd.pw/articles/2020-01/deploy-fastapi-to-aws-lambda). PR [#901](https://github.com/tiangolo/fastapi/pull/901) by [@iwpnd](https://github.com/iwpnd).
|
||||
* Add note about using Body parameters without Pydantic. PR [#900](https://github.com/tiangolo/fastapi/pull/900) by [@pawamoy](https://github.com/pawamoy).
|
||||
* Fix Pydantic field clone logic. PR [#899](https://github.com/tiangolo/fastapi/pull/899) by [@deuce2367](https://github.com/deuce2367).
|
||||
* Fix link in middleware docs. PR [#893](https://github.com/tiangolo/fastapi/pull/893) by [@linchiwei123](https://github.com/linchiwei123).
|
||||
* Rename default API title from "Fast API" to "FastAPI" for consistency. PR [#890](https://github.com/tiangolo/fastapi/pull/890).
|
||||
|
||||
## 0.47.1
|
||||
|
||||
* Fix model filtering in `response_model`, cloning sub-models. PR [#889](https://github.com/tiangolo/fastapi/pull/889).
|
||||
* Fix FastAPI serialization of Pydantic models using ORM mode blocking the event loop. PR [#888](https://github.com/tiangolo/fastapi/pull/888).
|
||||
|
||||
## 0.47.0
|
||||
|
||||
* Refactor documentation to make a simpler and shorter [Tutorial - User Guide](https://fastapi.tiangolo.com/tutorial/) and an additional [Advanced User Guide](https://fastapi.tiangolo.com/advanced/) with all the additional docs. PR [#887](https://github.com/tiangolo/fastapi/pull/887).
|
||||
@@ -8,9 +84,9 @@
|
||||
* Fix handling form *path operation* parameters declared with pure classes like `list`, `tuple`, etc. PR [#856](https://github.com/tiangolo/fastapi/pull/856) by [@nsidnev](https://github.com/nsidnev).
|
||||
* Add request `body` to `RequestValidationError`, new docs: [Use the `RequestValidationError` body](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-the-requestvalidationerror-body). Initial PR [#853](https://github.com/tiangolo/fastapi/pull/853) by [@aviramha](https://github.com/aviramha).
|
||||
* Update [External Links](https://fastapi.tiangolo.com/external-links/) with new links and dynamic GitHub projects with `fastapi` topic. PR [#850](https://github.com/tiangolo/fastapi/pull/850).
|
||||
* Fix Peewee `contextvars` handling in docs: [SQL (Relational) Databases with Peewee](https://fastapi.tiangolo.com/tutorial/sql-databases-peewee/). PR [#879](https://github.com/tiangolo/fastapi/pull/879).
|
||||
* Fix Peewee `contextvars` handling in docs: [SQL (Relational) Databases with Peewee](https://fastapi.tiangolo.com/advanced/sql-databases-peewee/). PR [#879](https://github.com/tiangolo/fastapi/pull/879).
|
||||
* Setup development environment with Python's Venv and Flit, instead of requiring the extra Pipenv duplicating dependencies. Updated docs: [Development - Contributing](https://fastapi.tiangolo.com/contributing/). PR [#877](https://github.com/tiangolo/fastapi/pull/877).
|
||||
* Update docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/security/http-basic-auth/) to improve security against timing attacks. Initial PR [#807](https://github.com/tiangolo/fastapi/pull/807) by [@zwass](https://github.com/zwass).
|
||||
* Update docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/) to improve security against timing attacks. Initial PR [#807](https://github.com/tiangolo/fastapi/pull/807) by [@zwass](https://github.com/zwass).
|
||||
|
||||
## 0.46.0
|
||||
|
||||
@@ -24,12 +100,12 @@
|
||||
* Add support for subtypes of main types in `jsonable_encoder`, e.g. asyncpg's UUIDs. PR [#756](https://github.com/tiangolo/fastapi/pull/756) by [@RmStorm](https://github.com/RmStorm).
|
||||
* Fix usage of Pydantic's `HttpUrl` in docs. PR [#832](https://github.com/tiangolo/fastapi/pull/832) by [@Dustyposa](https://github.com/Dustyposa).
|
||||
* Fix Twitter links in docs. PR [#813](https://github.com/tiangolo/fastapi/pull/813) by [@justindujardin](https://github.com/justindujardin).
|
||||
* Add docs for correctly [using FastAPI with Peewee ORM](https://fastapi.tiangolo.com/tutorial/sql-databases-peewee/). Including how to overwrite parts of Peewee to correctly handle async threads. PR [#789](https://github.com/tiangolo/fastapi/pull/789).
|
||||
* Add docs for correctly [using FastAPI with Peewee ORM](https://fastapi.tiangolo.com/advanced/sql-databases-peewee/). Including how to overwrite parts of Peewee to correctly handle async threads. PR [#789](https://github.com/tiangolo/fastapi/pull/789).
|
||||
|
||||
## 0.45.0
|
||||
|
||||
* Add support for OpenAPI Callbacks:
|
||||
* New docs: [OpenAPI Callbacks](https://fastapi.tiangolo.com/tutorial/openapi-callbacks/).
|
||||
* New docs: [OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
|
||||
* Refactor generation of `operationId`s to be valid Python names (also valid variables in most languages).
|
||||
* Add `default_response_class` parameter to `APIRouter`.
|
||||
* Original PR [#722](https://github.com/tiangolo/fastapi/pull/722) by [@booooh](https://github.com/booooh).
|
||||
@@ -62,11 +138,11 @@
|
||||
## 0.43.0
|
||||
|
||||
* Update docs to reduce gender bias. PR [#645](https://github.com/tiangolo/fastapi/pull/645) by [@ticosax](https://github.com/ticosax).
|
||||
* Add docs about [overriding the `operationId` for all the *path operations*](https://fastapi.tiangolo.com/tutorial/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) based on their function name. PR [#642](https://github.com/tiangolo/fastapi/pull/642) by [@SKalt](https://github.com/SKalt).
|
||||
* Add docs about [overriding the `operationId` for all the *path operations*](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) based on their function name. PR [#642](https://github.com/tiangolo/fastapi/pull/642) by [@SKalt](https://github.com/SKalt).
|
||||
* Fix validators in models generating an incorrect key order. PR [#637](https://github.com/tiangolo/fastapi/pull/637) by [@jaddison](https://github.com/jaddison).
|
||||
* Generate correct OpenAPI docs for responses with no content. PR [#621](https://github.com/tiangolo/fastapi/pull/621) by [@brotskydotcom](https://github.com/brotskydotcom).
|
||||
* Remove `$` from Bash code blocks in docs for consistency. PR [#613](https://github.com/tiangolo/fastapi/pull/613) by [@nstapelbroek](https://github.com/nstapelbroek).
|
||||
* Add docs for [self-serving docs' (Swagger UI) static assets](https://fastapi.tiangolo.com/tutorial/extending-openapi/#self-hosting-javascript-and-css-for-docs), e.g. to use the docs offline, or without Internet. Initial PR [#557](https://github.com/tiangolo/fastapi/pull/557) by [@svalouch](https://github.com/svalouch).
|
||||
* Add docs for [self-serving docs' (Swagger UI) static assets](https://fastapi.tiangolo.com/advanced/extending-openapi/#self-hosting-javascript-and-css-for-docs), e.g. to use the docs offline, or without Internet. Initial PR [#557](https://github.com/tiangolo/fastapi/pull/557) by [@svalouch](https://github.com/svalouch).
|
||||
* Fix `black` linting after upgrade. PR [#682](https://github.com/tiangolo/fastapi/pull/682) by [@frankie567](https://github.com/frankie567).
|
||||
|
||||
## 0.42.0
|
||||
@@ -95,7 +171,7 @@
|
||||
* Run middleware-like code only for a subset of *path operations*.
|
||||
* Process a request before passing it to a *path operation function*. E.g. decompressing, deserializing, etc.
|
||||
* Processing a response after being generated by *path operation functions* but before returning it. E.g. adding custom headers, logging, adding extra metadata.
|
||||
* New docs section: [Custom Request and APIRoute class](https://fastapi.tiangolo.com/tutorial/custom-request-and-route/).
|
||||
* New docs section: [Custom Request and APIRoute class](https://fastapi.tiangolo.com/advanced/custom-request-and-route/).
|
||||
* PR [#589](https://github.com/tiangolo/fastapi/pull/589) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Fix preserving custom route class in routers when including other sub-routers. PR [#538](https://github.com/tiangolo/fastapi/pull/538) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
@@ -104,11 +180,11 @@
|
||||
* Add notes to docs about installing `python-multipart` when using forms. PR [#574](https://github.com/tiangolo/fastapi/pull/574) by [@sliptonic](https://github.com/sliptonic).
|
||||
* Generate OpenAPI schemas in alphabetical order. PR [#554](https://github.com/tiangolo/fastapi/pull/554) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Add support for truncating docstrings from *path operation functions*.
|
||||
* New docs at [Advanced description from docstring](https://fastapi.tiangolo.com/tutorial/path-operation-advanced-configuration/#advanced-description-from-docstring).
|
||||
* New docs at [Advanced description from docstring](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#advanced-description-from-docstring).
|
||||
* PR [#556](https://github.com/tiangolo/fastapi/pull/556) by [@svalouch](https://github.com/svalouch).
|
||||
* Fix `DOCTYPE` in HTML files generated for Swagger UI and ReDoc. PR [#537](https://github.com/tiangolo/fastapi/pull/537) by [@Trim21](https://github.com/Trim21).
|
||||
* Fix handling `4XX` responses overriding default `422` validation error responses. PR [#517](https://github.com/tiangolo/fastapi/pull/517) by [@tsouvarev](https://github.com/tsouvarev).
|
||||
* Fix typo in documentation for [Simple HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/security/http-basic-auth/#simple-http-basic-auth). PR [#514](https://github.com/tiangolo/fastapi/pull/514) by [@prostomarkeloff](https://github.com/prostomarkeloff).
|
||||
* Fix typo in documentation for [Simple HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/#simple-http-basic-auth). PR [#514](https://github.com/tiangolo/fastapi/pull/514) by [@prostomarkeloff](https://github.com/prostomarkeloff).
|
||||
* Fix incorrect documentation example in [first steps](https://fastapi.tiangolo.com/tutorial/first-steps/). PR [#511](https://github.com/tiangolo/fastapi/pull/511) by [@IgnatovFedor](https://github.com/IgnatovFedor).
|
||||
* Add support for Swagger UI [initOauth](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md) settings with the parameter `swagger_ui_init_oauth`. PR [#499](https://github.com/tiangolo/fastapi/pull/499) by [@zamiramir](https://github.com/zamiramir).
|
||||
|
||||
@@ -179,7 +255,7 @@
|
||||
|
||||
* Fix source code `limit` for example in [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/). PR [#366](https://github.com/tiangolo/fastapi/pull/366) by [@Smashman](https://github.com/Smashman).
|
||||
|
||||
* Update wording in docs about [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/). PR [#371](https://github.com/tiangolo/fastapi/pull/371) by [@cjw296](https://github.com/cjw296).
|
||||
* Update wording in docs about [OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/). PR [#371](https://github.com/tiangolo/fastapi/pull/371) by [@cjw296](https://github.com/cjw296).
|
||||
|
||||
* Update docs for `Enum`s to inherit from `str` and improve Swagger UI rendering. PR [#351](https://github.com/tiangolo/fastapi/pull/351).
|
||||
|
||||
@@ -245,7 +321,7 @@
|
||||
|
||||
* Fix handling an empty-body request with a required body param. PR [#311](https://github.com/tiangolo/fastapi/pull/311).
|
||||
|
||||
* Fix broken link in docs: [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/). PR [#306](https://github.com/tiangolo/fastapi/pull/306) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Fix broken link in docs: [Return a Response directly](https://fastapi.tiangolo.com/advanced/response-directly/). PR [#306](https://github.com/tiangolo/fastapi/pull/306) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
* Fix docs discrepancy in docs for [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). PR [#288](https://github.com/tiangolo/fastapi/pull/288) by [@awiddersheim](https://github.com/awiddersheim).
|
||||
|
||||
@@ -253,9 +329,9 @@
|
||||
|
||||
* Add support for declaring a `Response` parameter:
|
||||
* This allows declaring:
|
||||
* [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/).
|
||||
* [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/).
|
||||
* An HTTP Status Code different than the default: [Response - Change Status Code](https://fastapi.tiangolo.com/tutorial/response-change-status-code/).
|
||||
* [Response Cookies](https://fastapi.tiangolo.com/advanced/response-cookies/).
|
||||
* [Response Headers](https://fastapi.tiangolo.com/advanced/response-headers/).
|
||||
* An HTTP Status Code different than the default: [Response - Change Status Code](https://fastapi.tiangolo.com/advanced/response-change-status-code/).
|
||||
* All of this while still being able to return arbitrary objects (`dict`, DB model, etc).
|
||||
* Update attribution to Hug, for inspiring the `response` parameter pattern.
|
||||
* PR [#294](https://github.com/tiangolo/fastapi/pull/294).
|
||||
@@ -272,7 +348,7 @@
|
||||
|
||||
* Implement dependency overrides for testing.
|
||||
* This allows using overrides/mocks of dependencies during tests.
|
||||
* New docs: [Testing Dependencies with Overrides](https://fastapi.tiangolo.com/tutorial/testing-dependencies/).
|
||||
* New docs: [Testing Dependencies with Overrides](https://fastapi.tiangolo.com/advanced/testing-dependencies/).
|
||||
* PR [#291](https://github.com/tiangolo/fastapi/pull/291).
|
||||
|
||||
## 0.27.2
|
||||
@@ -344,7 +420,7 @@
|
||||
* `Path`
|
||||
* `Query`
|
||||
* ...as these are compatible with the WebSockets protocol (e.g. `Body` is not).
|
||||
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/tutorial/websockets/).
|
||||
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/).
|
||||
* PR [#178](https://github.com/tiangolo/fastapi/pull/178) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
* Upgrade the compatible version of Pydantic to `0.26.0`.
|
||||
@@ -411,16 +487,16 @@
|
||||
|
||||
* Upgrade OAuth2:
|
||||
* Upgrade Password flow using Bearer tokens to use the correct HTTP status code 401 `UNAUTHORIZED`, with `WWW-Authenticate` headers.
|
||||
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/tutorial/security/intro/).
|
||||
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
|
||||
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/advanced/security/).
|
||||
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
|
||||
* Update docs, images, tests.
|
||||
* PR [#188](https://github.com/tiangolo/fastapi/pull/188).
|
||||
|
||||
* Include [Hypercorn](https://gitlab.com/pgjones/hypercorn) as an alternative ASGI server in the docs. PR [#187](https://github.com/tiangolo/fastapi/pull/187).
|
||||
|
||||
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/tutorial/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
|
||||
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/advanced/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
|
||||
|
||||
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
|
||||
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/advanced/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/advanced/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
|
||||
|
||||
* Fix typos in docs. PR [#176](https://github.com/tiangolo/fastapi/pull/176) by [@chdsbd](https://github.com/chdsbd).
|
||||
|
||||
@@ -430,13 +506,13 @@
|
||||
|
||||
* Add docs:
|
||||
* How to use the `jsonable_encoder` in [JSON compatible encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
|
||||
* How to [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/).
|
||||
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/tutorial/custom-response/).
|
||||
* How to [Return a Response directly](https://fastapi.tiangolo.com/advanced/response-directly/).
|
||||
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/advanced/custom-response/).
|
||||
* PR [#184](https://github.com/tiangolo/fastapi/pull/184).
|
||||
|
||||
## 0.18.0
|
||||
|
||||
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
|
||||
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
|
||||
|
||||
* Upgrade HTTP Basic Auth handling with automatic headers (automatic browser login prompt). PR [#175](https://github.com/tiangolo/fastapi/pull/175).
|
||||
|
||||
@@ -454,7 +530,7 @@
|
||||
|
||||
## 0.16.0
|
||||
|
||||
* Upgrade *path operation* `doctsring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
|
||||
* Upgrade *path operation* `docstring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
|
||||
|
||||
* Refactor internal usage of Pydantic to use correct data types. PR [#164](https://github.com/tiangolo/fastapi/pull/164).
|
||||
|
||||
@@ -468,7 +544,7 @@
|
||||
|
||||
* Add support for multiple file uploads (as a single form field). New docs at: [Multiple file uploads](https://fastapi.tiangolo.com/tutorial/request-files/#multiple-file-uploads). PR [#158](https://github.com/tiangolo/fastapi/pull/158).
|
||||
|
||||
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/tutorial/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
|
||||
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/advanced/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
|
||||
|
||||
## 0.14.0
|
||||
|
||||
@@ -489,7 +565,7 @@
|
||||
* Improve `Security` handling, merging scopes when declaring `SecurityScopes`.
|
||||
* Allow using `SecurityBase` (like `OAuth2`) classes with `Depends` and still document them. `Security` now is needed only to declare `scopes`.
|
||||
* Updated docs about: [OAuth2 with Password (and hashing), Bearer with JWT tokens](https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/).
|
||||
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
|
||||
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
|
||||
* PR [#141](https://github.com/tiangolo/fastapi/pull/141).
|
||||
|
||||
## 0.12.1
|
||||
@@ -504,7 +580,7 @@
|
||||
|
||||
* Add additional `responses` parameter to *path operation decorators* to extend responses in OpenAPI (and API docs).
|
||||
* It also allows extending existing responses generated from `response_model`, declare other media types (like images), etc.
|
||||
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/tutorial/additional-responses/).
|
||||
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/advanced/additional-responses/).
|
||||
* `responses` can also be added to `.include_router()`, the updated docs are here: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#add-some-custom-tags-and-responses).
|
||||
* PR [#97](https://github.com/tiangolo/fastapi/pull/97) originally initiated by [@barsi](https://github.com/barsi).
|
||||
* Update `scripts/test-cov-html.sh` to allow passing extra parameters like `-vv`, for development.
|
||||
@@ -521,7 +597,7 @@
|
||||
|
||||
* Add Gitter chat, badge, links, etc. [https://gitter.im/tiangolo/fastapi](https://gitter.im/tiangolo/fastapi) . PR [#117](https://github.com/tiangolo/fastapi/pull/117).
|
||||
|
||||
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/tutorial/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
|
||||
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/advanced/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
|
||||
|
||||
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR [#92](https://github.com/tiangolo/fastapi/pull/92) by [@blueyed](https://github.com/blueyed).
|
||||
|
||||
@@ -541,7 +617,7 @@
|
||||
|
||||
## 0.10.1
|
||||
|
||||
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
|
||||
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/advanced/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
|
||||
|
||||
## 0.10.0
|
||||
|
||||
@@ -549,7 +625,7 @@
|
||||
|
||||
* Add support for `.websocket_route()` in `APIRouter`. PR [#100](https://github.com/tiangolo/fastapi/pull/100) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/tutorial/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
|
||||
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/advanced/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
|
||||
|
||||
## 0.9.1
|
||||
|
||||
@@ -603,7 +679,7 @@
|
||||
|
||||
* Add section about [History, Design and Future](https://fastapi.tiangolo.com/history-design-future/).
|
||||
|
||||
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/tutorial/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
|
||||
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/advanced/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
|
||||
|
||||
## 0.6.3
|
||||
|
||||
@@ -619,7 +695,7 @@
|
||||
|
||||
## 0.6.1
|
||||
|
||||
* Add docs for GraphQL: [https://fastapi.tiangolo.com/tutorial/graphql/](https://fastapi.tiangolo.com/tutorial/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
|
||||
* Add docs for GraphQL: [https://fastapi.tiangolo.com/advanced/graphql/](https://fastapi.tiangolo.com/advanced/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
|
||||
|
||||
## 0.6.0
|
||||
|
||||
@@ -641,7 +717,7 @@
|
||||
|
||||
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: [https://fastapi.tiangolo.com/tutorial/handling-errors/](https://fastapi.tiangolo.com/tutorial/handling-errors/). PR [#35](https://github.com/tiangolo/fastapi/pull/35).
|
||||
|
||||
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/tutorial/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
|
||||
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/advanced/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Add issue templates to simplify reporting bugs, getting help, etc: [#34](https://github.com/tiangolo/fastapi/pull/34).
|
||||
|
||||
@@ -649,7 +725,7 @@
|
||||
|
||||
## 0.4.0
|
||||
|
||||
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/](https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
|
||||
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/advanced/sub-applications-proxy/](https://fastapi.tiangolo.com/advanced/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
|
||||
|
||||
* Update [docs/tutorial for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/) including note about _DB Browser for SQLite_.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import Body, FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
from fastapi import Body, FastAPI, status
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -17,4 +16,4 @@ async def upsert_item(item_id: str, name: str = Body(None), size: int = Body(Non
|
||||
else:
|
||||
item = {"name": name, "size": size}
|
||||
items[item_id] = item
|
||||
return JSONResponse(status_code=HTTP_201_CREATED, content=item)
|
||||
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)
|
||||
|
||||
11
docs/src/advanced_middleware/tutorial001.py
Normal file
11
docs/src/advanced_middleware/tutorial001.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(HTTPSRedirectMiddleware)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
13
docs/src/advanced_middleware/tutorial002.py
Normal file
13
docs/src/advanced_middleware/tutorial002.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
11
docs/src/advanced_middleware/tutorial003.py
Normal file
11
docs/src/advanced_middleware/tutorial003.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return "somebigcontent"
|
||||
@@ -1,4 +1,4 @@
|
||||
from starlette.testclient import TestClient
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from .main import app
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from starlette.testclient import TestClient
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from .main_b import app
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from starlette.websockets import WebSocket
|
||||
from fastapi.testclient import TestClient
|
||||
from fastapi.websockets import WebSocket
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -17,3 +17,8 @@ app.add_middleware(
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import gzip
|
||||
from typing import Callable, List
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
from fastapi import Body, FastAPI, Request, Response
|
||||
from fastapi.routing import APIRoute
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class GzipRequest(Request):
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from typing import Callable, List
|
||||
|
||||
from fastapi import Body, FastAPI, HTTPException
|
||||
from fastapi import Body, FastAPI, HTTPException, Request, Response
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.routing import APIRoute
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class ValidationErrorLoggingRoute(APIRoute):
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import time
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import APIRouter, FastAPI
|
||||
from fastapi import APIRouter, FastAPI, Request, Response
|
||||
from fastapi.routing import APIRoute
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class TimedRoute(APIRoute):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import UJSONResponse
|
||||
from fastapi.responses import UJSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
9
docs/src/custom_response/tutorial001b.py
Normal file
9
docs/src/custom_response/tutorial001b.py
Normal file
@@ -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"}]
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
9
docs/src/custom_response/tutorial005.py
Normal file
9
docs/src/custom_response/tutorial005.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import PlainTextResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/", response_class=PlainTextResponse)
|
||||
async def main():
|
||||
return "Hello World"
|
||||
9
docs/src/custom_response/tutorial006.py
Normal file
9
docs/src/custom_response/tutorial006.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/typer")
|
||||
async def read_typer():
|
||||
return RedirectResponse("https://typer.tiangolo.com")
|
||||
14
docs/src/custom_response/tutorial007.py
Normal file
14
docs/src/custom_response/tutorial007.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def fake_video_streamer():
|
||||
for i in range(10):
|
||||
yield b"some fake video bytes"
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return StreamingResponse(fake_video_streamer())
|
||||
11
docs/src/custom_response/tutorial008.py
Normal file
11
docs/src/custom_response/tutorial008.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
some_file_path = "large-video-file.mp4"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def main():
|
||||
file_like = open(some_file_path, mode="rb")
|
||||
return StreamingResponse(file_like, media_type="video/mp4")
|
||||
10
docs/src/custom_response/tutorial009.py
Normal file
10
docs/src/custom_response/tutorial009.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
some_file_path = "large-video-file.mp4"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return FileResponse(some_file_path)
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi.openapi.docs import (
|
||||
get_swagger_ui_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
|
||||
class UnicornException(Exception):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.responses import PlainTextResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi import FastAPI, Request, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette import status
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import time
|
||||
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
from fastapi import FastAPI, Request
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi import FastAPI, status
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -15,6 +14,6 @@ class Item(BaseModel):
|
||||
tags: Set[str] = []
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, status_code=HTTP_201_CREATED)
|
||||
@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
|
||||
async def create_item(*, item: Item):
|
||||
return item
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI, File, UploadFile
|
||||
from starlette.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
from fastapi import FastAPI, Response, status
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -11,5 +9,5 @@ tasks = {"foo": "Listen to the Bar Fighters"}
|
||||
def get_or_create_task(task_id: str, response: Response):
|
||||
if task_id not in tasks:
|
||||
tasks[task_id] = "This didn't exist before"
|
||||
response.status_code = HTTP_201_CREATED
|
||||
response.status_code = status.HTTP_201_CREATED
|
||||
return tasks[task_id]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
from fastapi import FastAPI, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ from datetime import datetime
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
from fastapi import FastAPI, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
from fastapi import FastAPI, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
from fastapi import FastAPI, status
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/items/", status_code=HTTP_201_CREATED)
|
||||
@app.post("/items/", status_code=status.HTTP_201_CREATED)
|
||||
async def create_item(name: str):
|
||||
return {"name": name}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi import Depends, FastAPI, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
fake_users_db = {
|
||||
"johndoe": {
|
||||
@@ -58,7 +57,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
user = fake_decode_token(token)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi import Depends, FastAPI, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jwt import PyJWTError
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
# to get a string like this run:
|
||||
# openssl rand -hex 32
|
||||
@@ -89,7 +88,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
@@ -118,7 +117,7 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
|
||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security, status
|
||||
from fastapi.security import (
|
||||
OAuth2PasswordBearer,
|
||||
OAuth2PasswordRequestForm,
|
||||
@@ -11,7 +11,6 @@ from fastapi.security import (
|
||||
from jwt import PyJWTError
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
# to get a string like this run:
|
||||
# openssl rand -hex 32
|
||||
@@ -111,7 +110,7 @@ async def get_current_user(
|
||||
else:
|
||||
authenticate_value = f"Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
@@ -130,7 +129,7 @@ async def get_current_user(
|
||||
for scope in security_scopes.scopes:
|
||||
if scope not in token_data.scopes:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Not enough permissions",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import secrets
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi import Depends, FastAPI, HTTPException, status
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -14,7 +13,7 @@ def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
correct_password = secrets.compare_digest(credentials.password, "swordfish")
|
||||
if not (correct_username and correct_password):
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
headers={"WWW-Authenticate": "Basic"},
|
||||
)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi import Depends, FastAPI, HTTPException, Request, Response
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
from . import crud, models, schemas
|
||||
from .database import SessionLocal, engine
|
||||
|
||||
@@ -2,7 +2,6 @@ import time
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from starlette.requests import Request
|
||||
|
||||
from . import crud, database, models, schemas
|
||||
from .database import db_state_default
|
||||
@@ -16,7 +15,12 @@ app = FastAPI()
|
||||
sleep_time = 10
|
||||
|
||||
|
||||
def get_db():
|
||||
async def reset_db_state():
|
||||
database.db._state._state.set(db_state_default.copy())
|
||||
database.db._state.reset()
|
||||
|
||||
|
||||
def get_db(db_state=Depends(reset_db_state)):
|
||||
try:
|
||||
database.db.connect()
|
||||
yield
|
||||
@@ -25,14 +29,6 @@ def get_db():
|
||||
database.db.close()
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def reset_db_middleware(request: Request, call_next):
|
||||
database.db._state._state.set(db_state_default.copy())
|
||||
database.db._state.reset()
|
||||
response = await call_next(request)
|
||||
return response
|
||||
|
||||
|
||||
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
|
||||
def create_user(user: schemas.UserCreate):
|
||||
db_user = crud.get_user_by_email(email=user.email)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from starlette.templating import Jinja2Templates
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
from fastapi import FastAPI, Request
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
from starlette.websockets import WebSocket
|
||||
from fastapi import FastAPI, WebSocket
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from fastapi import Cookie, Depends, FastAPI, Header
|
||||
from starlette.responses import HTMLResponse
|
||||
from starlette.status import WS_1008_POLICY_VIOLATION
|
||||
from starlette.websockets import WebSocket
|
||||
from fastapi import Cookie, Depends, FastAPI, Header, WebSocket, status
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -56,7 +54,7 @@ async def get_cookie_or_client(
|
||||
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
|
||||
):
|
||||
if session is None and x_client is None:
|
||||
await websocket.close(code=WS_1008_POLICY_VIOLATION)
|
||||
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
||||
return session or x_client
|
||||
|
||||
|
||||
|
||||
22
docs/src/wsgi/tutorial001.py
Normal file
22
docs/src/wsgi/tutorial001.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from flask import Flask, escape, request
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||
|
||||
flask_app = Flask(__name__)
|
||||
|
||||
|
||||
@flask_app.route("/")
|
||||
def flask_main():
|
||||
name = request.args.get("name", "World")
|
||||
return f"Hello, {escape(name)} from Flask!"
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/v2")
|
||||
def read_main():
|
||||
return {"message": "Hello World"}
|
||||
|
||||
|
||||
app.mount("/v1", WSGIMiddleware(flask_app))
|
||||
@@ -139,3 +139,7 @@ The function parameters will be recognized as follows:
|
||||
* If the parameter is also declared in the **path**, it will be used as a path parameter.
|
||||
* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc) it will be interpreted as a **query** parameter.
|
||||
* If the parameter is declared to be of the type of a **Pydantic model**, it will be interpreted as a request **body**.
|
||||
|
||||
## Without Pydantic
|
||||
|
||||
If you don't want to use Pydantic models, you can also use **Body** parameters. See the docs for [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
|
||||
@@ -32,9 +32,9 @@ So, for everything to work correctly, it's better to specify explicitly the allo
|
||||
|
||||
## Use `CORSMiddleware`
|
||||
|
||||
You can configure it in your **FastAPI** application using Starlette's <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">`CORSMiddleware`</a>.
|
||||
You can configure it in your **FastAPI** application using the `CORSMiddleware`.
|
||||
|
||||
* Import it from Starlette.
|
||||
* Import `CORSMiddleware`.
|
||||
* Create a list of allowed origins (as strings).
|
||||
* Add it as a "middleware" to your **FastAPI** application.
|
||||
|
||||
@@ -44,12 +44,39 @@ You can also specify if your backend allows:
|
||||
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
||||
* Specific HTTP headers or all of them with the wildcard `"*"`.
|
||||
|
||||
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
|
||||
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
|
||||
{!./src/cors/tutorial001.py!}
|
||||
```
|
||||
|
||||
The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin.
|
||||
* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. eg. `'https://.*\.example\.org'`.
|
||||
* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods.
|
||||
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for CORS requests.
|
||||
* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`.
|
||||
* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`.
|
||||
* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `60`.
|
||||
|
||||
The middleware responds to two particular types of HTTP request...
|
||||
|
||||
### CORS preflight requests
|
||||
|
||||
These are any `OPTIONS` request with `Origin` and `Access-Control-Request-Method` headers.
|
||||
|
||||
In this case the middleware will intercept the incoming request and respond with appropriate CORS headers, and either a `200` or `400` response for informational purposes.
|
||||
|
||||
### Simple requests
|
||||
|
||||
Any request with an `Origin` header. In this case the middleware will pass the request through as normal, but will include appropriate CORS headers on the response.
|
||||
|
||||
## More info
|
||||
|
||||
For more details of what you can specify in `CORSMiddleware`, check <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's `CORSMiddleware` docs</a>.
|
||||
|
||||
For more info about <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, check the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Mozilla CORS documentation</a>.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.middleware.cors import CORSMiddleware`.
|
||||
|
||||
**FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette.
|
||||
|
||||
@@ -102,6 +102,79 @@ You can have any combinations of dependencies that you want.
|
||||
|
||||
**FastAPI** uses them internally to achieve this.
|
||||
|
||||
## Dependencies with `yield` and `HTTPException`
|
||||
|
||||
You saw that you can use dependencies with `yield` and have `try` blocks that catch exceptions.
|
||||
|
||||
It might be tempting to raise an `HTTPException` or similar in the exit code, after the `yield`. But **it won't work**.
|
||||
|
||||
The exit code in dependencies with `yield` is executed *after* [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. There's nothing catching exceptions thrown by your dependencies in the exit code (after the `yield`).
|
||||
|
||||
So, if you raise an `HTTPException` after the `yield`, the default (or any custom) exception handler that catches `HTTPException`s and returns an HTTP 400 response won't be there to catch that exception anymore.
|
||||
|
||||
This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks.
|
||||
|
||||
Background tasks are run *after* the response has been sent. So there's no way to raise an `HTTPException` because there's not even a way to change the response that is *already sent*.
|
||||
|
||||
But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with `yield`, and maybe log the error or report it to a remote tracking system.
|
||||
|
||||
If you have some code that you know could raise an exception, do the most normal/"Pythonic" thing and add a `try` block in that section of the code.
|
||||
|
||||
If you have custom exceptions that you would like to handle *before* returning the response and possibly modifying the response, maybe even raising an `HTTPException`, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
!!! tip
|
||||
You can still raise exceptions including `HTTPException` *before* the `yield`. But not after.
|
||||
|
||||
The sequence of execution is more or less like this diagram. Time flows from top to bottom. And each column is one of the parts interacting or executing code.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
participant client as Client
|
||||
participant handler as Exception handler
|
||||
participant dep as Dep with yield
|
||||
participant operation as Path Operation
|
||||
participant tasks as Background tasks
|
||||
|
||||
Note over client,tasks: Can raise exception for dependency, handled after response is sent
|
||||
Note over client,operation: Can raise HTTPException and can change the response
|
||||
client ->> dep: Start request
|
||||
Note over dep: Run code up to yield
|
||||
opt raise
|
||||
dep -->> handler: Raise HTTPException
|
||||
handler -->> client: HTTP error response
|
||||
dep -->> dep: Raise other exception
|
||||
end
|
||||
dep ->> operation: Run dependency, e.g. DB session
|
||||
opt raise
|
||||
operation -->> handler: Raise HTTPException
|
||||
handler -->> client: HTTP error response
|
||||
operation -->> dep: Raise other exception
|
||||
end
|
||||
operation ->> client: Return response to client
|
||||
Note over client,operation: Response is already sent, can't change it anymore
|
||||
opt Tasks
|
||||
operation -->> tasks: Send background tasks
|
||||
end
|
||||
opt Raise other exception
|
||||
tasks -->> dep: Raise other exception
|
||||
end
|
||||
Note over dep: After yield
|
||||
opt Handle other exception
|
||||
dep -->> dep: Handle exception, can't change response. E.g. close DB session.
|
||||
end
|
||||
```
|
||||
|
||||
!!! info
|
||||
Only **one response** will be sent to the client. It might be one of the error responses or it will be the response from the *path operation*.
|
||||
|
||||
After one of those responses is sent, no other response can be sent.
|
||||
|
||||
!!! tip
|
||||
This diagram shows `HTTPException`, but you could also raise any other exception for which you create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. And that exception would be handled by that custom exception handler instead of the dependency exit code.
|
||||
|
||||
But if you raise an exception that is not handled by the exception handlers, it will be handled by the exit code of the dependency.
|
||||
|
||||
## Context Managers
|
||||
|
||||
### What are "Context Managers"
|
||||
|
||||
@@ -82,6 +82,19 @@ Whenever a new request arrives, **FastAPI** will take care of:
|
||||
* Get the result from your function.
|
||||
* Assign that result to the parameter in your *path operation function*.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
common_parameters(["common_parameters"])
|
||||
read_items["/items/"]
|
||||
read_users["/users/"]
|
||||
|
||||
common_parameters --> read_items
|
||||
common_parameters --> read_users
|
||||
```
|
||||
|
||||
This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*.
|
||||
|
||||
!!! check
|
||||
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
|
||||
|
||||
@@ -154,7 +167,39 @@ Although the hierarchical dependency injection system is very simple to define a
|
||||
|
||||
You can define dependencies that in turn can define dependencies themselves.
|
||||
|
||||
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and your dependencies) and providing (injecting) the results at each step.
|
||||
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.
|
||||
|
||||
For example, let's say you have 4 API endpoints (*path operations*):
|
||||
|
||||
* `/items/public/`
|
||||
* `/items/private/`
|
||||
* `/users/{user_id}/activate`
|
||||
* `/items/pro/`
|
||||
|
||||
then you could add different permission requirements for each of them just with dependencies and sub-dependencies:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
current_user(["current_user"])
|
||||
active_user(["active_user"])
|
||||
admin_user(["admin_user"])
|
||||
paying_user(["paying_user"])
|
||||
|
||||
public["/items/public/"]
|
||||
private["/items/private/"]
|
||||
activate_user["/users/{user_id}/activate"]
|
||||
pro_items["/items/pro/"]
|
||||
|
||||
current_user --> active_user
|
||||
active_user --> admin_user
|
||||
active_user --> paying_user
|
||||
|
||||
current_user --> public
|
||||
active_user --> private
|
||||
admin_user --> activate_user
|
||||
paying_user --> pro_items
|
||||
```
|
||||
|
||||
## Integrated with **OpenAPI**
|
||||
|
||||
|
||||
@@ -44,6 +44,17 @@ Then we can use the dependency with:
|
||||
|
||||
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
query_extractor(["query_extractor"])
|
||||
query_or_cookie_extractor(["query_or_cookie_extractor"])
|
||||
|
||||
read_query["/items/"]
|
||||
|
||||
query_extractor --> query_or_cookie_extractor --> read_query
|
||||
```
|
||||
|
||||
## Using the same dependency multiple times
|
||||
|
||||
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
|
||||
|
||||
@@ -92,7 +92,7 @@ It will show a JSON starting with something like:
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "Fast API",
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
There are many situations in where you need to notify an error to the client that is using your API.
|
||||
There are many situations in where you need to notify an error to a client that is using your API.
|
||||
|
||||
This client could be a browser with a frontend, the code from someone else, an IoT device, etc.
|
||||
This client could be a browser with a frontend, a code from someone else, an IoT device, etc.
|
||||
|
||||
You could need to tell that client that:
|
||||
You could need to tell the client that:
|
||||
|
||||
* The client doesn't have enough privileges for that operation.
|
||||
* The client doesn't have access to that resource.
|
||||
@@ -90,7 +90,7 @@ And you want to handle this exception globally with FastAPI.
|
||||
|
||||
You could add a custom exception handler with `@app.exception_handler()`:
|
||||
|
||||
```Python hl_lines="6 7 8 14 15 16 17 18 24"
|
||||
```Python hl_lines="5 6 7 13 14 15 16 17 18 24"
|
||||
{!./src/handling_errors/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -104,6 +104,11 @@ So, you will receive a clean error, with an HTTP status code of `418` and a JSON
|
||||
{"message": "Oops! yolo did something. There goes a rainbow..."}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.requests import Request` and `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request`.
|
||||
|
||||
## Override the default exception handlers
|
||||
|
||||
**FastAPI** has some default exception handlers.
|
||||
@@ -172,17 +177,22 @@ The same way, you can override the `HTTPException` handler.
|
||||
|
||||
For example, you could want to return a plain text response instead of JSON for these errors:
|
||||
|
||||
```Python hl_lines="1 3 9 10 11 22"
|
||||
```Python hl_lines="3 4 9 10 11 22"
|
||||
{!./src/handling_errors/tutorial004.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import PlainTextResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||
|
||||
### Use the `RequestValidationError` body
|
||||
|
||||
The `RequestValidationError` contains the `body` it received with invalid data.
|
||||
|
||||
You could use it while developing your app to log the body and debug it, return it to the user, etc.
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="14"
|
||||
{!./src/handling_errors/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -231,7 +241,7 @@ So, you can keep raising **FastAPI**'s `HTTPException` as normally in your code.
|
||||
|
||||
But when you register an exception handler, you should register it for Starlette's `HTTPException`.
|
||||
|
||||
This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises an `HTTPException`, your handler will be able to catch and handle it.
|
||||
This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises a Starlette `HTTPException`, your handler will be able to catch and handle it.
|
||||
|
||||
In this example, to be able to have both `HTTPException`s in the same code, Starlette's exceptions is renamed to `StarletteHTTPException`:
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ A "middleware" is a function that works with every **request** before it is proc
|
||||
* It can do something to that **response** or run any needed code.
|
||||
* Then it returns the **response**.
|
||||
|
||||
!!! note "Technical Details"
|
||||
If you have dependencies with `yield`, the exit code will run *after* the middleware.
|
||||
|
||||
If there were any background tasks (documented later), they will run *after* all the middleware.
|
||||
|
||||
## Create a middleware
|
||||
|
||||
To create a middleware you use the decorator `@app.middleware("http")` on top of a function.
|
||||
@@ -21,15 +26,19 @@ The middleware function receives:
|
||||
* Then it returns the `response` generated by the corresponding *path operation*.
|
||||
* You can then modify further the `response` before returning it.
|
||||
|
||||
```Python hl_lines="9 10 12 15"
|
||||
```Python hl_lines="8 9 11 14"
|
||||
{!./src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">using the 'X-' prefix</a>.
|
||||
|
||||
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)
|
||||
](cors.md){.internal-link target=_blank}) using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's CORS docs</a>.
|
||||
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's CORS docs</a>.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.requests import Request`.
|
||||
|
||||
**FastAPI** provides it as a convenience for you, the developer. But it comes directly from Starlette.
|
||||
|
||||
### Before and after the `response`
|
||||
|
||||
@@ -39,19 +48,12 @@ And also after the `response` is generated, before returning it.
|
||||
|
||||
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
|
||||
|
||||
```Python hl_lines="11 13 14"
|
||||
```Python hl_lines="10 12 13"
|
||||
{!./src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Starlette's Middleware
|
||||
## Other middlewares
|
||||
|
||||
You can also add any other <a href="https://www.starlette.io/middleware/" class="external-link" target="_blank">Starlette Middleware</a>.
|
||||
You can later read more about other middlewares in the [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
|
||||
|
||||
These are classes instead of plain functions.
|
||||
|
||||
Including:
|
||||
|
||||
* `CORSMiddleware` (described in the next section).
|
||||
* `GZipMiddleware`.
|
||||
* `SentryMiddleware`.
|
||||
* ...and others.
|
||||
You will read about how to handle <abbr title="Cross-Origin Resource Sharing">CORS</abbr> with a middleware in the next section.
|
||||
|
||||
@@ -9,14 +9,19 @@ You can define the (HTTP) `status_code` to be used in the response of your *path
|
||||
|
||||
You can pass directly the `int` code, like `404`.
|
||||
|
||||
But if you don't remember what each number code is for, you can use the shortcut constants from `starlette`:
|
||||
But if you don't remember what each number code is for, you can use the shortcut constants in `status`:
|
||||
|
||||
```Python hl_lines="5 18"
|
||||
```Python hl_lines="3 17"
|
||||
{!./src/path_operation_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
That status code will be used in the response and will be added to the OpenAPI schema.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette import status`.
|
||||
|
||||
**FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||
|
||||
## Tags
|
||||
|
||||
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
||||
|
||||
@@ -132,6 +132,11 @@ You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||
|
||||
So, whenever Swagger UI supports multi-file uploads, or any other tools that supports OpenAPI, they will be compatible with **FastAPI**.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||
|
||||
## Recap
|
||||
|
||||
Use `File` to declare files to be uploaded as input parameters (as form data).
|
||||
|
||||
@@ -64,9 +64,9 @@ Let's see the previous example again:
|
||||
|
||||
But you don't have to memorize what each of these codes mean.
|
||||
|
||||
You can use the convenience variables from `starlette.status`.
|
||||
You can use the convenience variables from `fastapi.status`.
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
```Python hl_lines="1 6"
|
||||
{!./src/response_status_code/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -74,6 +74,11 @@ They are just a convenience, they hold the same number, but that way you can use
|
||||
|
||||
<img src="/img/tutorial/response-status-code/image02.png">
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette import status`.
|
||||
|
||||
**FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||
|
||||
## Changing the default
|
||||
|
||||
Later, in the **Advanced User Guide**, you will see how to return a different status code than the default you are declaring here.
|
||||
|
||||
@@ -86,7 +86,7 @@ And another utility to verify if a received password matches the hash stored.
|
||||
|
||||
And another one to authenticate and return a user.
|
||||
|
||||
```Python hl_lines="7 39 56 57 60 61 70 71 72 73 74 75 76"
|
||||
```Python hl_lines="7 48 55 56 59 60 69 70 71 72 73 74 75"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -115,7 +115,7 @@ Define a Pydantic Model that will be used in the token endpoint for the response
|
||||
|
||||
Create a utility function to generate a new access token.
|
||||
|
||||
```Python hl_lines="3 6 13 14 15 29 30 31 79 80 81 82 83 84 85 86 87"
|
||||
```Python hl_lines="3 6 12 13 14 28 29 30 78 79 80 81 82 83 84 85 86"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -127,7 +127,7 @@ Decode the received token, verify it, and return the current user.
|
||||
|
||||
If the token is invalid, return an HTTP error right away.
|
||||
|
||||
```Python hl_lines="90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107"
|
||||
```Python hl_lines="89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -137,7 +137,7 @@ Create a `timedelta` with the expiration time of the token.
|
||||
|
||||
Create a real JWT access token and return it.
|
||||
|
||||
```Python hl_lines="116 117 118 119 120 121 122 123 124 125 126 127 128 129"
|
||||
```Python hl_lines="115 116 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Now let's use the utilities provided by **FastAPI** to handle this.
|
||||
|
||||
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` for the path `/token`:
|
||||
|
||||
```Python hl_lines="2 75"
|
||||
```Python hl_lines="2 74"
|
||||
{!./src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -88,7 +88,7 @@ If there is no such user, we return an error saying "incorrect username or passw
|
||||
|
||||
For the error, we use the exception `HTTPException`:
|
||||
|
||||
```Python hl_lines="1 76 77 78"
|
||||
```Python hl_lines="1 75 76 77"
|
||||
{!./src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -116,7 +116,7 @@ If your database is stolen, the thief won't have your users' plaintext passwords
|
||||
|
||||
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
|
||||
|
||||
```Python hl_lines="79 80 81 82"
|
||||
```Python hl_lines="78 79 80 81"
|
||||
{!./src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -154,7 +154,7 @@ For this simple example, we are going to just be completely insecure and return
|
||||
|
||||
But for now, let's focus on the specific details we need.
|
||||
|
||||
```Python hl_lines="84"
|
||||
```Python hl_lines="83"
|
||||
{!./src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -179,7 +179,7 @@ Both of these dependencies will just return an HTTP error if the user doesn't ex
|
||||
|
||||
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
|
||||
|
||||
```Python hl_lines="57 58 59 60 61 62 63 64 65 68 69 70 71 88"
|
||||
```Python hl_lines="56 57 58 59 60 61 62 63 64 65 67 68 69 70 88"
|
||||
{!./src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -469,6 +469,8 @@ Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in
|
||||
|
||||
This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request.
|
||||
|
||||
But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank}
|
||||
|
||||
And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy.
|
||||
|
||||
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
|
||||
@@ -556,31 +558,31 @@ For example, in a background task worker with <a href="http://www.celeryproject.
|
||||
|
||||
* `sql_app/database.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases/sql_app/database.py!}
|
||||
```
|
||||
|
||||
* `sql_app/models.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases/sql_app/models.py!}
|
||||
```
|
||||
|
||||
* `sql_app/schemas.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
* `sql_app/crud.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases/sql_app/crud.py!}
|
||||
```
|
||||
|
||||
* `sql_app/main.py`:
|
||||
|
||||
```Python hl_lines=""
|
||||
```Python
|
||||
{!./src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -624,7 +626,7 @@ A "middleware" is basically a function that is always executed for each request,
|
||||
|
||||
The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished.
|
||||
|
||||
```Python hl_lines="16 17 18 19 20 21 22 23 24"
|
||||
```Python hl_lines="14 15 16 17 18 19 20 21 22"
|
||||
{!./src/sql_databases/sql_app/alt_main.py!}
|
||||
```
|
||||
|
||||
@@ -637,7 +639,7 @@ The middleware we'll add (just a function) will create a new SQLAlchemy `Session
|
||||
|
||||
### About `request.state`
|
||||
|
||||
<a href="https://www.starlette.io/requests/#other-state" class="external-link" target="_blank">`request.state` is a property of each Starlette `Request` object</a>. It is there to store arbitrary objects attached to the request itself, like the database session in this case.
|
||||
`request.state` is a property of each `Request` object. It is there to store arbitrary objects attached to the request itself, like the database session in this case. You can read more about it in <a href="https://www.starlette.io/requests/#other-state" class="external-link" target="_blank">Starlette's docs about `Request` state</a>.
|
||||
|
||||
For us in this case, it helps us ensure a single database session is used through all the request, and then closed afterwards (in the middleware).
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
You can serve static files automatically from a directory using <a href="https://www.starlette.io/staticfiles/" class="external-link" target="_blank">Starlette's Static Files</a>.
|
||||
You can serve static files automatically from a directory using `StaticFiles`.
|
||||
|
||||
## Install `aiofiles`
|
||||
|
||||
@@ -10,13 +10,18 @@ pip install aiofiles
|
||||
|
||||
## Use `StaticFiles`
|
||||
|
||||
* Import `StaticFiles` from Starlette.
|
||||
* Import `StaticFiles`.
|
||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
```Python hl_lines="2 6"
|
||||
{!./src/static_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.staticfiles import StaticFiles`.
|
||||
|
||||
**FastAPI** provides the same `starlette.staticfiles` as `fastapi.staticfiles` just as a convenience for you, the developer. But it actually comes directly from Starlette.
|
||||
|
||||
### What is "Mounting"
|
||||
|
||||
"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette's TestClient</a>, testing **FastAPI** applications is easy and enjoyable.
|
||||
Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, testing **FastAPI** applications is easy and enjoyable.
|
||||
|
||||
It is based on <a href="http://docs.python-requests.org" class="external-link" target="_blank">Requests</a>, so it's very familiar and intuitive.
|
||||
|
||||
@@ -6,7 +6,7 @@ With it, you can use <a href="https://docs.pytest.org/" class="external-link" ta
|
||||
|
||||
## Using `TestClient`
|
||||
|
||||
Import `TestClient` from `starlette.testclient`.
|
||||
Import `TestClient`.
|
||||
|
||||
Create a `TestClient` passing to it your **FastAPI**.
|
||||
|
||||
@@ -16,7 +16,7 @@ Use the `TestClient` object the same way as you do with `requests`.
|
||||
|
||||
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
|
||||
|
||||
```Python hl_lines="2 12 15 16 17 18"
|
||||
```Python hl_lines="2 12 15 16 17 18"
|
||||
{!./src/app_testing/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -27,6 +27,11 @@ Write simple `assert` statements with the standard Python expressions that you n
|
||||
|
||||
This allows you to use `pytest` directly without complications.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.testclient import TestClient`.
|
||||
|
||||
**FastAPI** provides the same `starlette.testclient` as `fastapi.testclient` just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||
|
||||
## Separating tests
|
||||
|
||||
In a real application, you probably would have your tests in a different file.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user