✨ Add support for docs translations (#1168)
* 🌐 Refactor file structure to support internationalization * ✅ Update tests changed after i18n * 🔀 Merge Typer style from master * 🔧 Update MkConfig with Typer-styles * 🎨 Format mkdocs.yml with cannonical form * 🎨 Format mkdocs.yml * 🔧 Update MkDocs config * ➕ Add docs translation scripts dependencies * ✨ Add Typer scripts to handle translations * ✨ Add missing translation snippet to include * ✨ Update contributing docs, add docs for translations * 🙈 Add docs_build to gitignore * 🔧 Update scripts with new locations and docs scripts * 👷 Update docs deploy action with translations * 📝 Add note about languages not supported in the theme * ✨ Add first translation, for Spanish
|
|
@ -16,8 +16,8 @@ jobs:
|
|||
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: Build Docs
|
||||
run: python3.7 ./scripts/docs.py build-all
|
||||
- name: Deploy to Netlify
|
||||
uses: nwtgck/actions-netlify@v1.0.3
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -14,3 +14,4 @@ test.db
|
|||
log.txt
|
||||
Pipfile.lock
|
||||
env3.*
|
||||
docs_build
|
||||
|
|
|
|||
|
|
@ -1,176 +0,0 @@
|
|||
First, you might want to see the basic ways to [help FastAPI and get help](help-fastapi.md){.internal-link target=_blank}.
|
||||
|
||||
## Developing
|
||||
|
||||
If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
|
||||
|
||||
### Virtual environment with `venv`
|
||||
|
||||
You can create a virtual environment in a directory using Python's `venv` module:
|
||||
|
||||
```console
|
||||
$ python -m venv env
|
||||
```
|
||||
|
||||
That will create a directory `./env/` with the Python binaries and then you will be able to install packages for that isolated environment.
|
||||
|
||||
### Activate the environment
|
||||
|
||||
Activate the new environment with:
|
||||
|
||||
```console
|
||||
$ source ./env/bin/activate
|
||||
```
|
||||
|
||||
Or in Windows' PowerShell:
|
||||
|
||||
```console
|
||||
$ .\env\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
Or if you use Bash for Windows (e.g. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
```console
|
||||
$ source ./env/Scripts/activate
|
||||
```
|
||||
|
||||
To check it worked, use:
|
||||
|
||||
```console
|
||||
$ which pip
|
||||
|
||||
some/directory/fastapi/env/bin/pip
|
||||
```
|
||||
|
||||
If it shows the `pip` binary at `env/bin/pip` then it worked. 🎉
|
||||
|
||||
Or in Windows PowerShell:
|
||||
|
||||
```console
|
||||
$ Get-Command pip
|
||||
|
||||
some/directory/fastapi/env/bin/pip
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Every time you install a new package with `pip` under that environment, activate the environment again.
|
||||
|
||||
This makes sure that if you use a terminal program installed by that package (like `flit`), you use the one from your local environment and not any other that could be installed globally.
|
||||
|
||||
### Flit
|
||||
|
||||
**FastAPI** uses <a href="https://flit.readthedocs.io/en/latest/index.html" class="external-link" target="_blank">Flit</a> to build, package and publish the project.
|
||||
|
||||
After activating the environment as described above, install `flit`:
|
||||
|
||||
```console
|
||||
$ pip install flit
|
||||
```
|
||||
|
||||
Now re-activate the environment to make sure you are using the `flit` you just installed (and not a global one).
|
||||
|
||||
And now use `flit` to install the development dependencies:
|
||||
|
||||
```console
|
||||
$ flit install --deps develop --symlink
|
||||
```
|
||||
|
||||
It will install all the dependencies and your local FastAPI in your local environment.
|
||||
|
||||
#### Using your local FastAPI
|
||||
|
||||
If you create a Python file that imports and uses FastAPI, and run it with the Python from your local environment, it will use your local FastAPI source code.
|
||||
|
||||
And if you update that local FastAPI source code, as it is installed with `--symlink`, when you run that Python file again, it will use the fresh version of FastAPI you just edited.
|
||||
|
||||
That way, you don't have to "install" your local version to be able to test every change.
|
||||
|
||||
### Format
|
||||
|
||||
There is a script that you can run that will format and clean all your code:
|
||||
|
||||
```console
|
||||
$ bash scripts/format.sh
|
||||
```
|
||||
|
||||
It will also auto-sort all your imports.
|
||||
|
||||
For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above:
|
||||
|
||||
```console
|
||||
$ flit install --symlink
|
||||
```
|
||||
|
||||
### Format imports
|
||||
|
||||
There is another script that formats all the imports and makes sure you don't have unused imports:
|
||||
|
||||
```console
|
||||
$ bash scripts/format-imports.sh
|
||||
```
|
||||
|
||||
As it runs one command after the other and modifies and reverts many files, it takes a bit longer to run, so it might be easier to use `scripts/format.sh` frequently and `scripts/format-imports.sh` only before committing.
|
||||
|
||||
## Docs
|
||||
|
||||
The documentation uses <a href="https://www.mkdocs.org/" class="external-link" target="_blank">MkDocs</a>.
|
||||
|
||||
All the documentation is in Markdown format in the directory `./docs`.
|
||||
|
||||
Many of the tutorials have blocks of code.
|
||||
|
||||
In most of the cases, these blocks of code are actual complete applications that can be run as is.
|
||||
|
||||
In fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs/src/` directory.
|
||||
|
||||
And those Python files are included/injected in the documentation when generating the site.
|
||||
|
||||
### Docs for tests
|
||||
|
||||
Most of the tests actually run against the example source files in the documentation.
|
||||
|
||||
This helps making sure that:
|
||||
|
||||
* The documentation is up to date.
|
||||
* The documentation examples can be run as is.
|
||||
* Most of the features are covered by the documentation, ensured by test coverage.
|
||||
|
||||
During local development, there is a script that builds the site and checks for any changes, live-reloading:
|
||||
|
||||
```console
|
||||
$ bash scripts/docs-live.sh
|
||||
```
|
||||
|
||||
It will serve the documentation on `http://0.0.0.0:8008`.
|
||||
|
||||
That way, you can edit the documentation/source files and see the changes live.
|
||||
|
||||
### Apps and docs at the same time
|
||||
|
||||
If you run the examples with, e.g.:
|
||||
|
||||
```console
|
||||
$ uvicorn tutorial001:app --reload
|
||||
```
|
||||
|
||||
as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash.
|
||||
|
||||
## Tests
|
||||
|
||||
There is a script that you can run locally to test all the code and generate coverage reports in HTML:
|
||||
|
||||
```console
|
||||
$ bash scripts/test-cov-html.sh
|
||||
```
|
||||
|
||||
This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing.
|
||||
|
||||
### Tests in your editor
|
||||
|
||||
If you want to use the integrated tests in your editor add `./docs/src` to your `PYTHONPATH` variable.
|
||||
|
||||
For example, in VS Code you can create a file `.env` with:
|
||||
|
||||
```env
|
||||
PYTHONPATH=./docs/src
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Additional Responses in OpenAPI
|
||||
|
||||
!!! warning
|
||||
This is a rather advanced topic.
|
||||
|
||||
|
|
@ -22,7 +24,7 @@ Each of those response `dict`s can have a key `model`, containing a Pydantic mod
|
|||
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
|
||||
|
||||
```Python hl_lines="18 23"
|
||||
{!./src/additional_responses/tutorial001.py!}
|
||||
{!../../../docs_src/additional_responses/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -167,7 +169,7 @@ You can use this same `responses` parameter to add different media types for the
|
|||
For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image:
|
||||
|
||||
```Python hl_lines="17 18 19 20 21 22 23 24 28"
|
||||
{!./src/additional_responses/tutorial002.py!}
|
||||
{!../../../docs_src/additional_responses/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -191,7 +193,7 @@ For example, you can declare a response with a status code `404` that uses a Pyd
|
|||
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25 26 27 28 29 30 31"
|
||||
{!./src/additional_responses/tutorial003.py!}
|
||||
{!../../../docs_src/additional_responses/tutorial003.py!}
|
||||
```
|
||||
|
||||
It will all be combined and included in your OpenAPI, and shown in the API docs:
|
||||
|
|
@ -227,7 +229,7 @@ You can use that technique to re-use some predefined responses in your *path ope
|
|||
For example:
|
||||
|
||||
```Python hl_lines="11 12 13 14 15 24"
|
||||
{!./src/additional_responses/tutorial004.py!}
|
||||
{!../../../docs_src/additional_responses/tutorial004.py!}
|
||||
```
|
||||
|
||||
## More information about OpenAPI responses
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Additional Status Codes
|
||||
|
||||
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*.
|
||||
|
|
@ -13,7 +15,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 19"
|
||||
{!./src/additional_status_codes/tutorial001.py!}
|
||||
{!../../../docs_src/additional_status_codes/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Advanced Dependencies
|
||||
|
||||
## Parameterized dependencies
|
||||
|
||||
All the dependencies we have seen are a fixed function or class.
|
||||
|
|
@ -17,7 +19,7 @@ Not the class itself (which is already a callable), but an instance of that clas
|
|||
To do that, we declare a method `__call__`:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
{!../../../docs_src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
|
||||
|
|
@ -27,7 +29,7 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
|
|||
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
{!../../../docs_src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
|
||||
|
|
@ -37,7 +39,7 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
|
|||
We could create an instance of this class with:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
{!../../../docs_src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
|
||||
|
|
@ -55,7 +57,7 @@ checker(q="somequery")
|
|||
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
{!../../../docs_src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Async SQL (Relational) Databases
|
||||
|
||||
You can also use <a href="https://github.com/encode/databases" class="external-link" target="_blank">`encode/databases`</a> with **FastAPI** to connect to databases using `async` and `await`.
|
||||
|
||||
It is compatible with:
|
||||
|
|
@ -22,7 +24,7 @@ Later, for your production application, you might want to use a database server
|
|||
* Create a table `notes` using the `metadata` object.
|
||||
|
||||
```Python hl_lines="4 14 16 17 18 19 20 21 22"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -37,7 +39,7 @@ Later, for your production application, you might want to use a database server
|
|||
* Create a `database` object.
|
||||
|
||||
```Python hl_lines="3 9 12"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -53,7 +55,7 @@ Here, this section would run directly, right before starting your **FastAPI** ap
|
|||
* Create all the tables from the `metadata` object.
|
||||
|
||||
```Python hl_lines="25 26 27 28"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create models
|
||||
|
|
@ -64,7 +66,7 @@ Create Pydantic models for:
|
|||
* Notes to be returned (`Note`).
|
||||
|
||||
```Python hl_lines="31 32 33 36 37 38 39"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
By creating these Pydantic models, the input data will be validated, serialized (converted), and annotated (documented).
|
||||
|
|
@ -77,7 +79,7 @@ So, you will be able to see it all in the interactive API docs.
|
|||
* Create event handlers to connect and disconnect from the database.
|
||||
|
||||
```Python hl_lines="42 45 46 47 50 51 52"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Read notes
|
||||
|
|
@ -85,7 +87,7 @@ So, you will be able to see it all in the interactive API docs.
|
|||
Create the *path operation function* to read notes:
|
||||
|
||||
```Python hl_lines="55 56 57 58"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! Note
|
||||
|
|
@ -102,7 +104,7 @@ That documents (and validates, serializes, filters) the output data, as a `list`
|
|||
Create the *path operation function* to create notes:
|
||||
|
||||
```Python hl_lines="61 62 63 64 65"
|
||||
{!./src/async_sql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! Note
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Custom Request and APIRoute class
|
||||
|
||||
In some cases, you may want to override the logic used by the `Request` and `APIRoute` classes.
|
||||
|
||||
In particular, this may be a good alternative to logic in a middleware.
|
||||
|
|
@ -35,7 +37,7 @@ 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="8 9 10 11 12 13 14 15"
|
||||
{!./src/custom_request_and_route/tutorial001.py!}
|
||||
{!../../../docs_src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Create a custom `GzipRoute` class
|
||||
|
|
@ -49,7 +51,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="18 19 20 21 22 23 24 25 26"
|
||||
{!./src/custom_request_and_route/tutorial001.py!}
|
||||
{!../../../docs_src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
|
|
@ -83,13 +85,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="13 15"
|
||||
{!./src/custom_request_and_route/tutorial002.py!}
|
||||
{!../../../docs_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="16 17 18"
|
||||
{!./src/custom_request_and_route/tutorial002.py!}
|
||||
{!../../../docs_src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Custom `APIRoute` class in a router
|
||||
|
|
@ -97,11 +99,11 @@ 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="26"
|
||||
{!./src/custom_request_and_route/tutorial003.py!}
|
||||
{!../../../docs_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="13 14 15 16 17 18 19 20"
|
||||
{!./src/custom_request_and_route/tutorial003.py!}
|
||||
{!../../../docs_src/custom_request_and_route/tutorial003.py!}
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Custom Response - HTML, Stream, File, others
|
||||
|
||||
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}.
|
||||
|
|
@ -20,7 +22,7 @@ For example, if you are squeezing performance, you can install and use <a href="
|
|||
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/tutorial001b.py!}
|
||||
{!../../../docs_src/custom_response/tutorial001b.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -41,7 +43,7 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
|||
* Pass `HTMLResponse` as the parameter `content_type` of your *path operation*.
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
{!./src/custom_response/tutorial002.py!}
|
||||
{!../../../docs_src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -58,7 +60,7 @@ As seen in [Return a Response directly](response-directly.md){.internal-link tar
|
|||
The same example from above, returning an `HTMLResponse`, could look like:
|
||||
|
||||
```Python hl_lines="2 7 19"
|
||||
{!./src/custom_response/tutorial003.py!}
|
||||
{!../../../docs_src/custom_response/tutorial003.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
@ -78,7 +80,7 @@ The `response_class` will then be used only to document the OpenAPI *path operat
|
|||
For example, it could be something like:
|
||||
|
||||
```Python hl_lines="7 23 21"
|
||||
{!./src/custom_response/tutorial004.py!}
|
||||
{!../../../docs_src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
|
||||
|
|
@ -116,7 +118,7 @@ It accepts the following parameters:
|
|||
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!}
|
||||
{!../../../docs_src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
### `HTMLResponse`
|
||||
|
|
@ -128,7 +130,7 @@ Takes some text or bytes and returns an HTML response, as you read above.
|
|||
Takes some text or bytes and returns an plain text response.
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!./src/custom_response/tutorial005.py!}
|
||||
{!../../../docs_src/custom_response/tutorial005.py!}
|
||||
```
|
||||
|
||||
### `JSONResponse`
|
||||
|
|
@ -149,7 +151,7 @@ An alternative JSON response using <a href="https://github.com/ultrajson/ultrajs
|
|||
`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!}
|
||||
{!../../../docs_src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -160,7 +162,7 @@ An alternative JSON response using <a href="https://github.com/ultrajson/ultrajs
|
|||
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
|
||||
|
||||
```Python hl_lines="2 9"
|
||||
{!./src/custom_response/tutorial006.py!}
|
||||
{!../../../docs_src/custom_response/tutorial006.py!}
|
||||
```
|
||||
|
||||
### `StreamingResponse`
|
||||
|
|
@ -168,7 +170,7 @@ Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default
|
|||
Takes an async generator or a normal generator/iterator and streams the response body.
|
||||
|
||||
```Python hl_lines="2 14"
|
||||
{!./src/custom_response/tutorial007.py!}
|
||||
{!../../../docs_src/custom_response/tutorial007.py!}
|
||||
```
|
||||
|
||||
#### Using `StreamingResponse` with file-like objects
|
||||
|
|
@ -178,7 +180,7 @@ If you have a file-like object (e.g. the object returned by `open()`), you can r
|
|||
This includes many libraries to interact with cloud storage, video processing, and others.
|
||||
|
||||
```Python hl_lines="2 10 11"
|
||||
{!./src/custom_response/tutorial008.py!}
|
||||
{!../../../docs_src/custom_response/tutorial008.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -198,7 +200,7 @@ Takes a different set of arguments to instantiate than the other response types:
|
|||
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
|
||||
|
||||
```Python hl_lines="2 10"
|
||||
{!./src/custom_response/tutorial009.py!}
|
||||
{!../../../docs_src/custom_response/tutorial009.py!}
|
||||
```
|
||||
|
||||
## Additional documentation
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# Events: startup - shutdown
|
||||
|
||||
You can define event handlers (functions) that need to be executed before the application starts up, or when the application is shutting down.
|
||||
|
||||
|
|
@ -8,7 +9,7 @@ These functions can be declared with `async def` or normal `def`.
|
|||
To add a function that should be run before the application starts, declare it with the event `"startup"`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./src/events/tutorial001.py!}
|
||||
{!../../../docs_src/events/tutorial001.py!}
|
||||
```
|
||||
|
||||
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
|
||||
|
|
@ -22,7 +23,7 @@ And your application won't start receiving requests until all the `startup` even
|
|||
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/events/tutorial002.py!}
|
||||
{!../../../docs_src/events/tutorial002.py!}
|
||||
```
|
||||
|
||||
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Extending OpenAPI
|
||||
|
||||
!!! warning
|
||||
This is a rather advanced feature. You probably can skip it.
|
||||
|
||||
|
|
@ -43,7 +45,7 @@ For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/doc
|
|||
First, write all your **FastAPI** application as normally:
|
||||
|
||||
```Python hl_lines="1 4 7 8 9"
|
||||
{!./src/extending_openapi/tutorial001.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Generate the OpenAPI schema
|
||||
|
|
@ -51,7 +53,7 @@ First, write all your **FastAPI** application as normally:
|
|||
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
|
||||
|
||||
```Python hl_lines="2 15 16 17 18 19 20"
|
||||
{!./src/extending_openapi/tutorial001.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Modify the OpenAPI schema
|
||||
|
|
@ -59,7 +61,7 @@ Then, use the same utility function to generate the OpenAPI schema, inside a `cu
|
|||
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
|
||||
|
||||
```Python hl_lines="21 22 23"
|
||||
{!./src/extending_openapi/tutorial001.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Cache the OpenAPI schema
|
||||
|
|
@ -71,7 +73,7 @@ That way, your application won't have to generate the schema every time a user o
|
|||
It will be generated only once, and then the same cached schema will be used for the next requests.
|
||||
|
||||
```Python hl_lines="13 14 24 25"
|
||||
{!./src/extending_openapi/tutorial001.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Override the method
|
||||
|
|
@ -79,7 +81,7 @@ It will be generated only once, and then the same cached schema will be used for
|
|||
Now you can replace the `.openapi()` method with your new function.
|
||||
|
||||
```Python hl_lines="28"
|
||||
{!./src/extending_openapi/tutorial001.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Check it
|
||||
|
|
@ -172,7 +174,7 @@ $ pip install aiofiles
|
|||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
```Python hl_lines="7 11"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Test the static files
|
||||
|
|
@ -206,7 +208,7 @@ The first step is to disable the automatic docs, as those use the CDN by default
|
|||
To disable them, set their URLs to `None` when creating your `FastAPI` app:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Include the custom docs
|
||||
|
|
@ -224,7 +226,7 @@ You can re-use FastAPI's internal functions to create the HTML pages for the doc
|
|||
And similarly for ReDoc...
|
||||
|
||||
```Python hl_lines="2 3 4 5 6 14 15 16 17 18 19 20 21 22 25 26 27 30 31 32 33 34 35 36"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -239,7 +241,7 @@ And similarly for ReDoc...
|
|||
Now, to be able to test that everything works, create a *path operation*:
|
||||
|
||||
```Python hl_lines="39 40 41"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Test it
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# GraphQL
|
||||
|
||||
**FastAPI** has optional support for GraphQL (provided by Starlette directly), using the `graphene` library.
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ GraphQL is implemented with Graphene, you can check <a href="https://docs.graphe
|
|||
Import `graphene` and define your GraphQL data:
|
||||
|
||||
```Python hl_lines="1 6 7 8 9 10"
|
||||
{!./src/graphql/tutorial001.py!}
|
||||
{!../../../docs_src/graphql/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Add Starlette's `GraphQLApp`
|
||||
|
|
@ -18,7 +19,7 @@ Import `graphene` and define your GraphQL data:
|
|||
Then import and add Starlette's `GraphQLApp`:
|
||||
|
||||
```Python hl_lines="3 14"
|
||||
{!./src/graphql/tutorial001.py!}
|
||||
{!../../../docs_src/graphql/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Advanced User Guide - Intro
|
||||
|
||||
## Additional Features
|
||||
|
||||
The main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank} should be enough to give you a tour through all the main features of **FastAPI**.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Advanced Middleware
|
||||
|
||||
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}.
|
||||
|
|
@ -53,7 +55,7 @@ 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!}
|
||||
{!../../../docs_src/advanced_middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
## `TrustedHostMiddleware`
|
||||
|
|
@ -61,7 +63,7 @@ Any incoming requests to `http` or `ws` will be redirected to the secure scheme
|
|||
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!}
|
||||
{!../../../docs_src/advanced_middleware/tutorial002.py!}
|
||||
```
|
||||
|
||||
The following arguments are supported:
|
||||
|
|
@ -77,7 +79,7 @@ Handles GZip responses for any request that includes `"gzip"` in the `Accept-Enc
|
|||
The middleware will handle both standard and streaming responses.
|
||||
|
||||
```Python hl_lines="2 6 7 8"
|
||||
{!./src/advanced_middleware/tutorial002.py!}
|
||||
{!../../../docs_src/advanced_middleware/tutorial002.py!}
|
||||
```
|
||||
|
||||
The following arguments are supported:
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# NoSQL (Distributed / Big Data) Databases
|
||||
|
||||
**FastAPI** can also be integrated with any <abbr title="Distributed database (Big Data), also 'Not Only SQL'">NoSQL</abbr>.
|
||||
|
||||
Here we'll see an example using **<a href="https://www.couchbase.com/" class="external-link" target="_blank">Couchbase</a>**, a <abbr title="Document here refers to a JSON object (a dict), with keys and values, and those values can also be other JSON objects, arrays (lists), numbers, strings, booleans, etc.">document</abbr> based NoSQL database.
|
||||
|
|
@ -18,7 +20,7 @@ You can adapt it to any other NoSQL database like:
|
|||
For now, don't pay attention to the rest, only the imports:
|
||||
|
||||
```Python hl_lines="6 7 8"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Define a constant to use as a "document type"
|
||||
|
|
@ -28,7 +30,7 @@ We will use it later as a fixed field `type` in our documents.
|
|||
This is not required by Couchbase, but is a good practice that will help you afterwards.
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Add a function to get a `Bucket`
|
||||
|
|
@ -53,7 +55,7 @@ This utility function will:
|
|||
* Return it.
|
||||
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20 21 22"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create Pydantic models
|
||||
|
|
@ -65,7 +67,7 @@ As **Couchbase** "documents" are actually just "JSON objects", we can model them
|
|||
First, let's create a `User` model:
|
||||
|
||||
```Python hl_lines="25 26 27 28 29"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
We will use this model in our *path operation function*, so, we don't include in it the `hashed_password`.
|
||||
|
|
@ -79,7 +81,7 @@ This will have the data that is actually stored in the database.
|
|||
We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more:
|
||||
|
||||
```Python hl_lines="32 33 34"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -99,7 +101,7 @@ Now create a function that will:
|
|||
By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your *path operation function*, you can more easily re-use it in multiple parts and also add <abbr title="Automated test, written in code, that checks if another piece of code is working correctly.">unit tests</abbr> for it:
|
||||
|
||||
```Python hl_lines="37 38 39 40 41 42 43"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
### f-strings
|
||||
|
|
@ -134,7 +136,7 @@ UserInDB(username="johndoe", hashed_password="some_hash")
|
|||
### Create the `FastAPI` app
|
||||
|
||||
```Python hl_lines="47"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Create the *path operation function*
|
||||
|
|
@ -144,7 +146,7 @@ As our code is calling Couchbase and we are not using the <a href="https://docs.
|
|||
Also, Couchbase recommends not using a single `Bucket` object in multiple "<abbr title="A sequence of code being executed by the program, while at the same time, or at intervals, there can be others being executed too.">thread</abbr>s", so, we can get just get the bucket directly and pass it to our utility functions:
|
||||
|
||||
```Python hl_lines="50 51 52 53 54"
|
||||
{!./src/nosql_databases/tutorial001.py!}
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# OpenAPI Callbacks
|
||||
|
||||
You could create an API with a *path operation* that could trigger a request to an *external API* created by someone else (probably the same developer that would be *using* your API).
|
||||
|
||||
The process that happens when your API app calls the *external API* is named a "callback". Because the software that the external developer wrote sends a request to your API and then your API *calls back*, sending a request to an *external API* (that was probably created by the same developer).
|
||||
|
|
@ -30,7 +32,7 @@ It will have a *path operation* that will receive an `Invoice` body, and a query
|
|||
This part is pretty normal, most of the code is probably already familiar to you:
|
||||
|
||||
```Python hl_lines="8 9 10 11 12 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -91,7 +93,7 @@ Because of that, you need to declare what will be the `default_response_class`,
|
|||
But as we are never calling `app.include_router(some_router)`, we need to set the `default_response_class` during creation of the `APIRouter`.
|
||||
|
||||
```Python hl_lines="3 24"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Create the callback *path operation*
|
||||
|
|
@ -104,7 +106,7 @@ It should look just like a normal FastAPI *path operation*:
|
|||
* And it could also have a declaration of the response it should return, e.g. `response_model=InvoiceEventReceived`.
|
||||
|
||||
```Python hl_lines="15 16 17 20 21 27 28 29 30 31"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
There are 2 main differences from a normal *path operation*:
|
||||
|
|
@ -171,7 +173,7 @@ At this point you have the *callback path operation(s)* needed (the one(s) that
|
|||
Now use the parameter `callbacks` in *your API's path operation decorator* to pass the attribute `.routes` (that's actually just a `list` of routes/*path operations*) from that callback router:
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Path Operation Advanced Configuration
|
||||
|
||||
## OpenAPI operationId
|
||||
|
||||
!!! warning
|
||||
|
|
@ -8,7 +10,7 @@ You can set the OpenAPI `operationId` to be used in your *path operation* with t
|
|||
You would have to make sure that it is unique for each operation.
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/path_operation_advanced_configuration/tutorial001.py!}
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Using the *path operation function* name as the operationId
|
||||
|
|
@ -18,7 +20,7 @@ If you want to use your APIs' function names as `operationId`s, you can iterate
|
|||
You should do it after adding all your *path operations*.
|
||||
|
||||
```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24"
|
||||
{!./src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -34,7 +36,7 @@ You should do it after adding all your *path operations*.
|
|||
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`;
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/path_operation_advanced_configuration/tutorial003.py!}
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Advanced description from docstring
|
||||
|
|
@ -46,5 +48,5 @@ Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate
|
|||
It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest.
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29"
|
||||
{!./src/path_operation_advanced_configuration/tutorial004.py!}
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Response - Change Status Code
|
||||
|
||||
You probably read before that you can set a default [Response Status Code](../tutorial/response-status-code.md){.internal-link target=_blank}.
|
||||
|
||||
But in some cases you need to return a different status code than the default.
|
||||
|
|
@ -19,7 +21,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="1 9 12"
|
||||
{!./src/response_change_status_code/tutorial001.py!}
|
||||
{!../../../docs_src/response_change_status_code/tutorial001.py!}
|
||||
```
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Response Cookies
|
||||
|
||||
## Use a `Response` parameter
|
||||
|
||||
You can declare a parameter of type `Response` in your *path operation function*.
|
||||
|
|
@ -5,7 +7,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
|||
And then you can set cookies in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="1 8 9"
|
||||
{!./src/response_cookies/tutorial002.py!}
|
||||
{!../../../docs_src/response_cookies/tutorial002.py!}
|
||||
```
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
|
@ -25,7 +27,7 @@ To do that, you can create a response as described in [Return a Response Directl
|
|||
Then set Cookies in it, and then return it:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
{!./src/response_cookies/tutorial001.py!}
|
||||
{!../../../docs_src/response_cookies/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Return a Response Directly
|
||||
|
||||
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
|
||||
|
||||
By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
|
||||
|
|
@ -30,7 +32,7 @@ For example, you cannot put a Pydantic model in a `JSONResponse` without first c
|
|||
For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response:
|
||||
|
||||
```Python hl_lines="4 6 20 21"
|
||||
{!./src/response_directly/tutorial001.py!}
|
||||
{!../../../docs_src/response_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
|
|
@ -49,7 +51,7 @@ Let's say that you want to return an <a href="https://en.wikipedia.org/wiki/XML"
|
|||
You could put your XML content in a string, put it in a `Response`, and return it:
|
||||
|
||||
```Python hl_lines="1 18"
|
||||
{!./src/response_directly/tutorial002.py!}
|
||||
{!../../../docs_src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Response Headers
|
||||
|
||||
## Use a `Response` parameter
|
||||
|
||||
You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies).
|
||||
|
|
@ -5,7 +7,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="1 7 8"
|
||||
{!./src/response_headers/tutorial002.py!}
|
||||
{!../../../docs_src/response_headers/tutorial002.py!}
|
||||
```
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
|
@ -23,7 +25,7 @@ You can also add headers when you return a `Response` directly.
|
|||
Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
{!./src/response_headers/tutorial001.py!}
|
||||
{!../../../docs_src/response_headers/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# HTTP Basic Auth
|
||||
|
||||
For the simplest cases, you can use HTTP Basic Auth.
|
||||
|
||||
In HTTP Basic Auth, the application expects a header that contains a username and a password.
|
||||
|
|
@ -19,7 +21,7 @@ Then, when you type that username and password, the browser sends them in the he
|
|||
* It contains the `username` and `password` sent.
|
||||
|
||||
```Python hl_lines="2 6 10"
|
||||
{!./src/security/tutorial006.py!}
|
||||
{!../../../docs_src/security/tutorial006.py!}
|
||||
```
|
||||
|
||||
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
|
||||
|
|
@ -35,7 +37,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 11 12 13"
|
||||
{!./src/security/tutorial007.py!}
|
||||
{!../../../docs_src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
This will ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`. This would be similar to:
|
||||
|
|
@ -101,5 +103,5 @@ 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="15 16 17 18 19"
|
||||
{!./src/security/tutorial007.py!}
|
||||
{!../../../docs_src/security/tutorial007.py!}
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Advanced Security - Intro
|
||||
|
||||
## Additional Features
|
||||
|
||||
There are some extra features to handle security apart from the ones covered in the [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# OAuth2 scopes
|
||||
|
||||
You can use OAuth2 scopes directly with **FastAPI**, they are integrated to work seamlessly.
|
||||
|
||||
This would allow you to have a more fine-grained permission system, following the OAuth2 standard, integrated into your OpenAPI application (and the API docs).
|
||||
|
|
@ -55,7 +57,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 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!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
Now let's review those changes step by step.
|
||||
|
|
@ -67,7 +69,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="63 64 65 66"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize.
|
||||
|
|
@ -92,7 +94,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="155"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Declare scopes in *path operations* and dependencies
|
||||
|
|
@ -117,7 +119,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 140 167"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! info "Technical Details"
|
||||
|
|
@ -142,7 +144,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 106"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Use the `scopes`
|
||||
|
|
@ -158,7 +160,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="106 108 109 110 111 112 113 114 115 116"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Verify the `username` and data shape
|
||||
|
|
@ -176,7 +178,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="47 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Verify the `scopes`
|
||||
|
|
@ -186,7 +188,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="129 130 131 132 133 134 135"
|
||||
{!./src/security/tutorial005.py!}
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Dependency tree and scopes
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# SQL (Relational) Databases with Peewee
|
||||
|
||||
!!! warning
|
||||
If you are just starting, the tutorial [SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank} that uses SQLAlchemy should be enough.
|
||||
|
||||
|
|
@ -60,7 +62,7 @@ Let's refer to the file `sql_app/database.py`.
|
|||
Let's first check all the normal Peewee code, create a Peewee database:
|
||||
|
||||
```Python hl_lines="3 5 22"
|
||||
{!./src/sql_databases_peewee/sql_app/database.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/database.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -112,7 +114,7 @@ This might seem a bit complex (and it actually is), you don't really need to com
|
|||
We will create a `PeeweeConnectionState`:
|
||||
|
||||
```Python hl_lines="10 11 12 13 14 15 16 17 18 19"
|
||||
{!./src/sql_databases_peewee/sql_app/database.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/database.py!}
|
||||
```
|
||||
|
||||
This class inherits from a special internal class used by Peewee.
|
||||
|
|
@ -133,7 +135,7 @@ So, we need to do some extra tricks to make it work as if it was just using `thr
|
|||
Now, overwrite the `._state` internal attribute in the Peewee database `db` object using the new `PeeweeConnectionState`:
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!./src/sql_databases_peewee/sql_app/database.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/database.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -160,7 +162,7 @@ This is the same you would do if you followed the Peewee tutorial and updated th
|
|||
Import `db` from `database` (the file `database.py` from above) and use it here.
|
||||
|
||||
```Python hl_lines="3 6 7 8 9 10 11 12 15 16 17 18 19 20 21"
|
||||
{!./src/sql_databases_peewee/sql_app/models.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/models.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -188,7 +190,7 @@ Now let's check the file `sql_app/schemas.py`.
|
|||
Create all the same Pydantic models as in the SQLAlchemy tutorial:
|
||||
|
||||
```Python hl_lines="16 17 18 21 22 25 26 27 28 29 30 34 35 38 39 42 43 44 45 46 47 48"
|
||||
{!./src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -213,7 +215,7 @@ But recent versions of Pydantic allow providing a custom class that inherits fro
|
|||
We are going to create a custom `PeeweeGetterDict` class and use it in all the same Pydantic *models* / schemas that use `orm_mode`:
|
||||
|
||||
```Python hl_lines="3 8 9 10 11 12 13 31 49"
|
||||
{!./src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
Here we are checking if the attribute that is being accessed (e.g. `.items` in `some_user.items`) is an instance of `peewee.ModelSelect`.
|
||||
|
|
@ -234,7 +236,7 @@ Now let's see the file `sql_app/crud.py`.
|
|||
Create all the same CRUD utils as in the SQLAlchemy tutorial, all the code is very similar:
|
||||
|
||||
```Python hl_lines="1 4 5 8 9 12 13 16 17 18 19 20 23 24 27 28 29 30"
|
||||
{!./src/sql_databases_peewee/sql_app/crud.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/crud.py!}
|
||||
```
|
||||
|
||||
There are some differences with the code for the SQLAlchemy tutorial.
|
||||
|
|
@ -258,7 +260,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="9 10 11"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
### Create a dependency
|
||||
|
|
@ -266,7 +268,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="23 24 25 26 27 28 29"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
Here we have an empty `yield` because we are actually not using the database object directly.
|
||||
|
|
@ -280,7 +282,7 @@ And then, in each *path operation function* that needs to access the database we
|
|||
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="32 40 47 59 65 72"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
### Context variable sub-dependency
|
||||
|
|
@ -290,7 +292,7 @@ For all the `contextvars` parts to work, we need to make sure we have an indepen
|
|||
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).
|
||||
|
||||
```Python hl_lines="18 19 20"
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
{!../../../docs_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).
|
||||
|
|
@ -319,7 +321,7 @@ async def reset_db_state():
|
|||
Now, finally, here's the standard **FastAPI** *path operations* code.
|
||||
|
||||
```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!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
### About `def` vs `async def`
|
||||
|
|
@ -436,31 +438,31 @@ Repeat the same process with the 10 tabs. This time all of them will wait and yo
|
|||
* `sql_app/database.py`:
|
||||
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/database.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/database.py!}
|
||||
```
|
||||
|
||||
* `sql_app/models.py`:
|
||||
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/models.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/models.py!}
|
||||
```
|
||||
|
||||
* `sql_app/schemas.py`:
|
||||
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
* `sql_app/crud.py`:
|
||||
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/crud.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/crud.py!}
|
||||
```
|
||||
|
||||
* `sql_app/main.py`:
|
||||
|
||||
```Python
|
||||
{!./src/sql_databases_peewee/sql_app/main.py!}
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Sub Applications - Behind a Proxy, Mounts
|
||||
|
||||
There are at least two situations where you could need to create your **FastAPI** application using some specific paths.
|
||||
|
||||
But then you need to set them up to be served with a path prefix.
|
||||
|
|
@ -44,7 +46,7 @@ You could want to do this if you have several "independent" applications that yo
|
|||
First, create the main, top-level, **FastAPI** application, and its *path operations*:
|
||||
|
||||
```Python hl_lines="3 6 7 8"
|
||||
{!./src/sub_applications/tutorial001.py!}
|
||||
{!../../../docs_src/sub_applications/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Sub-application
|
||||
|
|
@ -56,7 +58,7 @@ This sub-application is just another standard FastAPI application, but this is t
|
|||
When creating the sub-application, use the parameter `openapi_prefix`. In this case, with a prefix of `/subapi`:
|
||||
|
||||
```Python hl_lines="11 14 15 16"
|
||||
{!./src/sub_applications/tutorial001.py!}
|
||||
{!../../../docs_src/sub_applications/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Mount the sub-application
|
||||
|
|
@ -66,7 +68,7 @@ In your top-level application, `app`, mount the sub-application, `subapi`.
|
|||
Here you need to make sure you use the same path that you used for the `openapi_prefix`, in this case, `/subapi`:
|
||||
|
||||
```Python hl_lines="11 19"
|
||||
{!./src/sub_applications/tutorial001.py!}
|
||||
{!../../../docs_src/sub_applications/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Check the automatic API docs
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Templates
|
||||
|
||||
You can use any template engine you want with **FastAPI**.
|
||||
|
||||
A common election is Jinja2, the same one used by Flask and other tools.
|
||||
|
|
@ -38,7 +40,7 @@ $ pip install aiofiles
|
|||
* 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="3 10 14 15"
|
||||
{!./src/templates/tutorial001.py!}
|
||||
{!../../../docs_src/templates/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -54,7 +56,7 @@ $ pip install aiofiles
|
|||
Then you can write a template at `templates/item.html` with:
|
||||
|
||||
```jinja hl_lines="7"
|
||||
{!./src/templates/templates/item.html!}
|
||||
{!../../../docs_src/templates/templates/item.html!}
|
||||
```
|
||||
|
||||
It will show the `id` taken from the "context" `dict` you passed:
|
||||
|
|
@ -68,13 +70,13 @@ It will show the `id` taken from the "context" `dict` you passed:
|
|||
And you can also use `url_for()` inside of the template, and use it, for example, with the `StaticFiles` you mounted.
|
||||
|
||||
```jinja hl_lines="4"
|
||||
{!./src/templates/templates/item.html!}
|
||||
{!../../../docs_src/templates/templates/item.html!}
|
||||
```
|
||||
|
||||
In this example, it would link to a CSS file at `static/styles.css` with:
|
||||
|
||||
```CSS hl_lines="4"
|
||||
{!./src/templates/static/styles.css!}
|
||||
{!../../../docs_src/templates/static/styles.css!}
|
||||
```
|
||||
|
||||
And because you are using `StaticFiles`, that CSS file would be served automatically by your **FastAPI** application at the URL `/static/styles.css`.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Testing Dependencies with Overrides
|
||||
|
||||
## Overriding dependencies during testing
|
||||
|
||||
There are some scenarios where you might want to override a dependency during testing.
|
||||
|
|
@ -39,7 +41,7 @@ To override a dependency for testing, you put as a key the original dependency (
|
|||
And then **FastAPI** will call that override instead of the original dependency.
|
||||
|
||||
```Python hl_lines="24 25 28"
|
||||
{!./src/dependency_testing/tutorial001.py!}
|
||||
{!../../../docs_src/dependency_testing/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
## Testing Events, `startup` and `shutdown`
|
||||
# Testing Events: startup - shutdown
|
||||
|
||||
When you need your event handlers (`startup` and `shutdown`) to run in your tests, you can use the `TestClient` with a `with` statement:
|
||||
|
||||
```Python hl_lines="9 10 11 12 20 21 22 23 24"
|
||||
{!./src/app_testing/tutorial003.py!}
|
||||
```
|
||||
{!../../../docs_src/app_testing/tutorial003.py!}
|
||||
```
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
## Testing WebSockets
|
||||
# Testing WebSockets
|
||||
|
||||
You can use the same `TestClient` to test WebSockets.
|
||||
|
||||
For this, you use the `TestClient` in a `with` statement, connecting to the WebSocket:
|
||||
|
||||
```Python hl_lines="27 28 29 30 31"
|
||||
{!./src/app_testing/tutorial002.py!}
|
||||
{!../../../docs_src/app_testing/tutorial002.py!}
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Using the Request Directly
|
||||
|
||||
Up to now, you have been declaring the parts of the request that you need with their types.
|
||||
|
||||
Taking data from:
|
||||
|
|
@ -28,7 +30,7 @@ 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.
|
||||
|
||||
```Python hl_lines="1 7 8"
|
||||
{!./src/using_request_directly/tutorial001.py!}
|
||||
{!../../../docs_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.
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# WebSockets
|
||||
|
||||
You can use <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> with **FastAPI**.
|
||||
|
||||
|
|
@ -24,7 +25,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 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!}
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create a `websocket`
|
||||
|
|
@ -32,7 +33,7 @@ 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="1 46 47"
|
||||
{!./src/websockets/tutorial001.py!}
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
|
|
@ -45,7 +46,7 @@ In your **FastAPI** application, create a `websocket`:
|
|||
In your WebSocket route you can `await` for messages and send messages.
|
||||
|
||||
```Python hl_lines="48 49 50 51 52"
|
||||
{!./src/websockets/tutorial001.py!}
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
You can receive and send binary, text, and JSON data.
|
||||
|
|
@ -64,7 +65,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="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!}
|
||||
{!../../../docs_src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Including WSGI - Flask, Django, others
|
||||
|
||||
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.
|
||||
|
|
@ -11,7 +13,7 @@ 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!}
|
||||
{!../../../docs_src/wsgi/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Check it
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Alternatives, Inspiration and Comparisons
|
||||
|
||||
What inspired **FastAPI**, how it compares to other alternatives and what it learned from them.
|
||||
|
||||
## Intro
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Concurrency and async / await
|
||||
|
||||
Details about the `async def` syntax for *path operation functions* and some background about asynchronous code, concurrency, and parallelism.
|
||||
|
||||
## In a hurry?
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Benchmarks
|
||||
|
||||
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
|
||||
|
||||
But when checking benchmarks and comparisons you should have the following in mind.
|
||||
|
|
@ -0,0 +1,441 @@
|
|||
# Development - Contributing
|
||||
|
||||
First, you might want to see the basic ways to [help FastAPI and get help](help-fastapi.md){.internal-link target=_blank}.
|
||||
|
||||
## Developing
|
||||
|
||||
If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
|
||||
|
||||
### Virtual environment with `venv`
|
||||
|
||||
You can create a virtual environment in a directory using Python's `venv` module:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m venv env
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
That will create a directory `./env/` with the Python binaries and then you will be able to install packages for that isolated environment.
|
||||
|
||||
### Activate the environment
|
||||
|
||||
Activate the new environment with:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source ./env/bin/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Or in Windows' PowerShell:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ .\env\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Or if you use Bash for Windows (e.g. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source ./env/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
To check it worked, use:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which pip
|
||||
|
||||
some/directory/fastapi/env/bin/pip
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
If it shows the `pip` binary at `env/bin/pip` then it worked. 🎉
|
||||
|
||||
Or in Windows PowerShell:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ Get-Command pip
|
||||
|
||||
some/directory/fastapi/env/bin/pip
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
!!! tip
|
||||
Every time you install a new package with `pip` under that environment, activate the environment again.
|
||||
|
||||
This makes sure that if you use a terminal program installed by that package (like `flit`), you use the one from your local environment and not any other that could be installed globally.
|
||||
|
||||
### Flit
|
||||
|
||||
**FastAPI** uses <a href="https://flit.readthedocs.io/en/latest/index.html" class="external-link" target="_blank">Flit</a> to build, package and publish the project.
|
||||
|
||||
After activating the environment as described above, install `flit`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install flit
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Now re-activate the environment to make sure you are using the `flit` you just installed (and not a global one).
|
||||
|
||||
And now use `flit` to install the development dependencies:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ flit install --deps develop --symlink
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
It will install all the dependencies and your local FastAPI in your local environment.
|
||||
|
||||
#### Using your local FastAPI
|
||||
|
||||
If you create a Python file that imports and uses FastAPI, and run it with the Python from your local environment, it will use your local FastAPI source code.
|
||||
|
||||
And if you update that local FastAPI source code, as it is installed with `--symlink`, when you run that Python file again, it will use the fresh version of FastAPI you just edited.
|
||||
|
||||
That way, you don't have to "install" your local version to be able to test every change.
|
||||
|
||||
### Format
|
||||
|
||||
There is a script that you can run that will format and clean all your code:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ bash scripts/format.sh
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
It will also auto-sort all your imports.
|
||||
|
||||
For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ flit install --symlink
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Format imports
|
||||
|
||||
There is another script that formats all the imports and makes sure you don't have unused imports:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ bash scripts/format-imports.sh
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
As it runs one command after the other and modifies and reverts many files, it takes a bit longer to run, so it might be easier to use `scripts/format.sh` frequently and `scripts/format-imports.sh` only before committing.
|
||||
|
||||
## Docs
|
||||
|
||||
First, make sure you set up your environment as described above, that will install all the requirements.
|
||||
|
||||
The documentation uses <a href="https://www.mkdocs.org/" class="external-link" target="_blank">MkDocs</a>.
|
||||
|
||||
And there are extra tools/scripts in place to handle translations in `./scripts/docs.py`.
|
||||
|
||||
!!! tip
|
||||
You don't need to see the code in `./scripts/docs.py`, you just use it in the command line.
|
||||
|
||||
All the documentation is in Markdown format in the directory `./docs/en/`.
|
||||
|
||||
Many of the tutorials have blocks of code.
|
||||
|
||||
In most of the cases, these blocks of code are actual complete applications that can be run as is.
|
||||
|
||||
In fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs_src/` directory.
|
||||
|
||||
And those Python files are included/injected in the documentation when generating the site.
|
||||
|
||||
### Docs for tests
|
||||
|
||||
Most of the tests actually run against the example source files in the documentation.
|
||||
|
||||
This helps making sure that:
|
||||
|
||||
* The documentation is up to date.
|
||||
* The documentation examples can be run as is.
|
||||
* Most of the features are covered by the documentation, ensured by test coverage.
|
||||
|
||||
During local development, there is a script that builds the site and checks for any changes, live-reloading:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python ./scripts/docs.py live
|
||||
|
||||
<span style="color: green;">[INFO]</span> Serving on http://0.0.0.0:8008
|
||||
<span style="color: green;">[INFO]</span> Start watching changes
|
||||
<span style="color: green;">[INFO]</span> Start detecting changes
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
It will serve the documentation on `http://0.0.0.0:8008`.
|
||||
|
||||
That way, you can edit the documentation/source files and see the changes live.
|
||||
|
||||
#### Typer CLI (optional)
|
||||
|
||||
The instructions here show you how to use the script at `./scripts/docs.py` with the `python` program directly.
|
||||
|
||||
But you can also use <a href="https://typer.tiangolo.com/typer-cli/" class="external-link" target="_blank">Typer CLI</a>, and you will get autocompletion in your terminal for the commands after installing completion.
|
||||
|
||||
If you install Typer CLI, you can install completion with:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ typer --install-completion
|
||||
|
||||
zsh completion installed in /home/user/.bashrc.
|
||||
Completion will take effect once you restart the terminal.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Apps and docs at the same time
|
||||
|
||||
If you run the examples with, e.g.:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn tutorial001:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash.
|
||||
|
||||
### Translations
|
||||
|
||||
Help with translations is VERY MUCH appreciated! And it can't be done without the help from the community. 🌎 🚀
|
||||
|
||||
Here are the steps to help with translations.
|
||||
|
||||
#### Tips
|
||||
|
||||
✨ Add a single Pull Request per page translated. That will make it much easier for others to review it.
|
||||
|
||||
For the languages I don't speak, I'll wait for several others to review the translation before merging.
|
||||
|
||||
✨ You can also check if there are translations for your language and add a review to them, that will help me know that the translation is correct and I can merge it.
|
||||
|
||||
✨ To check the 2-letter code for the language you want to translate you can use the table <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" class="external-link" target="_blank">List of ISO 639-1 codes</a>.
|
||||
|
||||
#### Existing language
|
||||
|
||||
Let's say you want to translate a page for a language that already has translations for some pages, like Spanish.
|
||||
|
||||
In the case of Spanish, the 2-letter code is `es`. So, the directory for Spanish translations is located at `docs/es/`.
|
||||
|
||||
!!! tip
|
||||
The main ("official") language is English, located at `docs/en/`.
|
||||
|
||||
Now run the live server for the docs in Spanish:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Use the command "live" and pass the language code as a CLI argument
|
||||
$ python ./scripts/docs.py live es
|
||||
|
||||
<span style="color: green;">[INFO]</span> Serving on http://0.0.0.0:8008
|
||||
<span style="color: green;">[INFO]</span> Start watching changes
|
||||
<span style="color: green;">[INFO]</span> Start detecting changes
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Now you can go to <a href="http://0.0.0.0:8008" class="external-link" target="_blank">http://0.0.0.0:8008</a> and see your changes live.
|
||||
|
||||
If you look at the FastAPI docs website, you will see that every language has all the pages. But some are not translated and have a notification about the the translation is missing.
|
||||
|
||||
But when you run it locally like this, you will only see the pages that are already translated.
|
||||
|
||||
Now let's say that you want to add a translation for the section [Advanced User Guide: Extending OpenAPI](advanced/extending-openapi.md){.internal-link target=_blank}.
|
||||
|
||||
* Copy the file at:
|
||||
|
||||
```
|
||||
docs/en/docs/advanced/extending-openapi.md
|
||||
```
|
||||
|
||||
* Paste it in exactly the same location but for the language you want to translate, e.g.:
|
||||
|
||||
```
|
||||
docs/es/docs/advanced/extending-openapi.md
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Notice that the only change in the path and file name is the language code, from `en` to `es`.
|
||||
|
||||
* Now open the MkDocs config file at:
|
||||
|
||||
```
|
||||
docs/en/docs/mkdocs.yml
|
||||
```
|
||||
|
||||
* Find the place where that `advanced/extending-openapi.md` is located in the config file. Somewhere like:
|
||||
|
||||
```YAML hl_lines="9"
|
||||
site_name: FastAPI
|
||||
# More stuff
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
# More stuff
|
||||
- Advanced User Guide:
|
||||
# More stuff
|
||||
- advanced/testing-dependencies.md
|
||||
- advanced/extending-openapi.md
|
||||
- advanced/openapi-callbacks.md
|
||||
```
|
||||
|
||||
* Open the MkDocs config file for the language you are editing, e.g.:
|
||||
|
||||
```
|
||||
docs/es/docs/mkdocs.yml
|
||||
```
|
||||
|
||||
* Add the equivalent structure code and add it at the exact same location it would be, e.g.:
|
||||
|
||||
```YAML hl_lines="6 9"
|
||||
site_name: FastAPI
|
||||
# More stuff
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
# More stuff
|
||||
- Guía de Usuario Avanzada:
|
||||
# More stuff
|
||||
- advanced/testing-dependencies.md
|
||||
- advanced/extending-openapi.md
|
||||
- advanced/openapi-callbacks.md
|
||||
```
|
||||
|
||||
Notice that the `Advanced User Guide` is translated to `Guía de Usuario Avanzada`.
|
||||
|
||||
Also, make sure that if there are other entries, the new entry with your translation is in exactly in the same order as in the English version.
|
||||
|
||||
If you go to your browser you will see that now the docs show your new section. 🎉
|
||||
|
||||
Now you can translate it all and see how it looks as you save the file.
|
||||
|
||||
#### New Language
|
||||
|
||||
Let's say that you want to add translations for a language that is not yet translated, not even some pages.
|
||||
|
||||
Let's say you want to add translations for Creole, and it's not yet there in the docs.
|
||||
|
||||
Checking the link from above, the code for "Creole" is `ht`.
|
||||
|
||||
The next step is to run the script to generate a new translation directory:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Use the command new-lang, pass the language code as a CLI argument
|
||||
$ python ./scripts/docs.py new-lang ht
|
||||
|
||||
Successfully initialized: docs/ht
|
||||
Updating ht
|
||||
Updating en
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Now you can check in your code editor the newly created directory `docs/ht/`.
|
||||
|
||||
Start by translating the main page, `docs/ht/index.md`.
|
||||
|
||||
Then you can continue with the previous instructions, for an "Existing Language".
|
||||
|
||||
##### New Language not supported
|
||||
|
||||
If when running the live server script you get an error about the language not being supported, something like:
|
||||
|
||||
```
|
||||
raise TemplateNotFound(template)
|
||||
jinja2.exceptions.TemplateNotFound: partials/language/xx.html
|
||||
```
|
||||
|
||||
That means that the theme doesn't support that language (in this case, with a fake 2-letter code of `xx`).
|
||||
|
||||
But don't worry, you can set the theme language to English and then translate the content of the docs.
|
||||
|
||||
If you need to do that, edit the `mkdocs.yml` for your new language, it will have something like:
|
||||
|
||||
```YAML hl_lines="5"
|
||||
site_name: FastAPI
|
||||
# More stuff
|
||||
theme:
|
||||
# More stuff
|
||||
language: xx
|
||||
```
|
||||
|
||||
Change that language from `xx` (from your language code) to `en`.
|
||||
|
||||
Then you can start the live server again.
|
||||
|
||||
## Tests
|
||||
|
||||
There is a script that you can run locally to test all the code and generate coverage reports in HTML:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ bash scripts/test-cov-html.sh
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing.
|
||||
|
||||
### Tests in your editor
|
||||
|
||||
If you want to use the integrated tests in your editor add `./docs/src` to your `PYTHONPATH` variable.
|
||||
|
||||
For example, in VS Code you can create a file `.env` with:
|
||||
|
||||
```env
|
||||
PYTHONPATH=./docs/src
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Deployment
|
||||
|
||||
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.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# External Links and Articles
|
||||
|
||||
**FastAPI** has a great community constantly growing.
|
||||
|
||||
There are many posts, articles, tools, and projects, related to **FastAPI**.
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# Features
|
||||
|
||||
## FastAPI features
|
||||
|
||||
|
|
@ -16,11 +17,11 @@ Interactive API documentation and exploration web user interfaces. As the framew
|
|||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, with interactive exploration, call and test your API directly from the browser.
|
||||
|
||||

|
||||

|
||||
|
||||
* Alternative API documentation with <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
|
||||
|
||||

|
||||

|
||||
|
||||
### Just Modern Python
|
||||
|
||||
|
|
@ -82,11 +83,11 @@ Here's how your editor might help you:
|
|||
|
||||
* in <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
|
||||
|
||||

|
||||

|
||||
|
||||
* in <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
|
||||
|
||||

|
||||

|
||||
|
||||
You will get completion in code you might even consider impossible before. As for example, the `price` key inside a JSON body (that could have been nested) that comes from a request.
|
||||
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Help FastAPI - Get Help
|
||||
|
||||
Do you like **FastAPI**?
|
||||
|
||||
Would you like to help FastAPI, other users, and the author?
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# History, Design and Future
|
||||
|
||||
Some time ago, <a href="https://github.com/tiangolo/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">a **FastAPI** user asked</a>:
|
||||
|
||||
> What’s the history of this project? It seems to have come from nowhere to awesome in a few weeks [...]
|
||||
|
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 412 B |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |