Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0388915a8 | ||
|
|
9e2f5c67b6 | ||
|
|
93e4a19e35 | ||
|
|
e1c6d7d310 | ||
|
|
d23b295b96 | ||
|
|
26e94116c1 | ||
|
|
24968937e5 | ||
|
|
5c5b889288 | ||
|
|
436261b3ea | ||
|
|
5c62a59e7b | ||
|
|
acbe79da37 | ||
|
|
a85aa125d2 | ||
|
|
9af8cc69d5 | ||
|
|
7fe79441c1 | ||
|
|
b8ae84d460 | ||
|
|
ca2b1dbb64 | ||
|
|
672c55ac62 | ||
|
|
66c27c3e07 | ||
|
|
44f4885c66 | ||
|
|
4da33e3031 | ||
|
|
a1ede32f29 | ||
|
|
0a82b3a900 | ||
|
|
d08a062ee2 | ||
|
|
83f6781037 | ||
|
|
764ecae2d4 | ||
|
|
4e9f75912f | ||
|
|
2b10ca1cc4 | ||
|
|
3efb4f7edf | ||
|
|
9c25f9615c | ||
|
|
e7158bc592 | ||
|
|
6d642ef5fb | ||
|
|
b0cd4d7e7e | ||
|
|
0a87bc88b8 | ||
|
|
062efcbb5d | ||
|
|
aa4a69f790 | ||
|
|
eb79441a7f | ||
|
|
fa5639cb35 | ||
|
|
10e3a02582 | ||
|
|
fb9c4b31b9 | ||
|
|
39dbee9d56 | ||
|
|
dd463d0dc2 | ||
|
|
461e0d4cce | ||
|
|
da69a1c4c0 | ||
|
|
a5d697b9b5 | ||
|
|
8e416875ce | ||
|
|
446d194c46 | ||
|
|
cace5a79f7 | ||
|
|
dabb4898f1 | ||
|
|
f282c0e207 | ||
|
|
146ff28d9c | ||
|
|
58ab733f19 | ||
|
|
0a61a6c865 | ||
|
|
fb1f341231 | ||
|
|
652cf4bb6b | ||
|
|
08410ca568 | ||
|
|
0d5e0ba5d5 | ||
|
|
b2c9574fc6 | ||
|
|
a859497a72 | ||
|
|
3c93e803d5 | ||
|
|
0bd8f901df | ||
|
|
864643ef76 | ||
|
|
5d80e02ae8 | ||
|
|
affe542753 | ||
|
|
8e887d7145 | ||
|
|
46cf92c55a | ||
|
|
921b6f742a | ||
|
|
c1fee263e1 | ||
|
|
42d10ef4bf | ||
|
|
637cf89dbd | ||
|
|
a81e2f20a5 | ||
|
|
53076170c0 | ||
|
|
eb89968b36 | ||
|
|
c54fb7e208 | ||
|
|
077e5baf2c | ||
|
|
9c6e0d02bb | ||
|
|
d198f38b45 | ||
|
|
4ba1e81f16 | ||
|
|
b1508dcacc | ||
|
|
bb20f5e60d | ||
|
|
7e4887ea55 | ||
|
|
ffaecddbf6 | ||
|
|
e43f434448 | ||
|
|
eb453a50b8 | ||
|
|
5a28f95a93 | ||
|
|
4093c35058 | ||
|
|
7efdc11075 | ||
|
|
24cda290e8 | ||
|
|
d06b4d7bb8 | ||
|
|
1a8284725a | ||
|
|
4f168c5ada | ||
|
|
b67532ee5c | ||
|
|
c29aa0bc87 | ||
|
|
5551e7282c | ||
|
|
8e68ddbddf | ||
|
|
46882b9ae1 | ||
|
|
e423c8241a | ||
|
|
3581034eff | ||
|
|
b69781b161 | ||
|
|
12db3926b5 | ||
|
|
afe1f69e79 | ||
|
|
426bd096ad | ||
|
|
0ceacef413 | ||
|
|
fee3126d3a | ||
|
|
8f4e3a4377 | ||
|
|
3b686c3774 | ||
|
|
17f0ec8927 | ||
|
|
a6ef62ce7b | ||
|
|
d9fa2311b3 | ||
|
|
c15f042318 | ||
|
|
a8bde38f7c | ||
|
|
496ccc0f70 | ||
|
|
11d0a08acd | ||
|
|
4d26fa5c54 | ||
|
|
eb1d68c789 | ||
|
|
292b8c8ce9 | ||
|
|
cce8d9e32c | ||
|
|
ac0f594305 | ||
|
|
cc0d0f3899 | ||
|
|
67d5ba1efd | ||
|
|
2210c84efd | ||
|
|
378fa4ef75 | ||
|
|
eb515b1af7 | ||
|
|
19fd336cde | ||
|
|
63744b2e8a | ||
|
|
85f0d2b924 | ||
|
|
507e9baf9e | ||
|
|
c6461ad7dc | ||
|
|
5fbf597cd5 | ||
|
|
bc99d2b7b8 | ||
|
|
0eb27ab4d0 | ||
|
|
68c43eb126 | ||
|
|
00ac07f65c | ||
|
|
d8b0a9dc6e | ||
|
|
8c36572d23 | ||
|
|
e0e3224588 | ||
|
|
4e2595f928 | ||
|
|
6de8748788 | ||
|
|
39ec5b33d9 | ||
|
|
557fe61d92 | ||
|
|
458a7fbf15 | ||
|
|
ae22bca9fe | ||
|
|
9229ba8078 | ||
|
|
96ca425b1f | ||
|
|
c86a4e1dd3 | ||
|
|
24749aef71 | ||
|
|
f6a3bc2902 | ||
|
|
ba8c78d87f | ||
|
|
69784e5141 | ||
|
|
8a02a47124 | ||
|
|
5607c198c4 | ||
|
|
cf5e67590a | ||
|
|
655db2af1f | ||
|
|
8c102814fd | ||
|
|
189ac3e280 | ||
|
|
2680f369d0 | ||
|
|
284eb6615d | ||
|
|
099c478655 | ||
|
|
54c02d402a | ||
|
|
4b968c4e39 | ||
|
|
1db0fc2953 | ||
|
|
d29aa3d436 | ||
|
|
1b6350ad9e | ||
|
|
cb7e79ab8a | ||
|
|
fe086a4903 | ||
|
|
64e7deaebc | ||
|
|
bee35f5ae1 | ||
|
|
3a786a7a3a | ||
|
|
eaeafc32c4 | ||
|
|
c8b4e4d455 | ||
|
|
1760da0efa | ||
|
|
f49ba24b71 |
27
.github/actions/notify-translations/app/main.py
vendored
@@ -1,5 +1,7 @@
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
import random
|
||||
from typing import Dict, Optional
|
||||
|
||||
import yaml
|
||||
@@ -45,8 +47,11 @@ if __name__ == "__main__":
|
||||
github_event = PartialGitHubEvent.parse_raw(contents)
|
||||
translations_map: Dict[str, int] = yaml.safe_load(translations_path.read_text())
|
||||
logging.debug(f"Using translations map: {translations_map}")
|
||||
sleep_time = random.random() * 10 # random number between 0 and 10 seconds
|
||||
pr = repo.get_pull(github_event.pull_request.number)
|
||||
logging.debug(f"Processing PR: {pr.number}")
|
||||
logging.debug(
|
||||
f"Processing PR: {pr.number}, with anti-race condition sleep time: {sleep_time}"
|
||||
)
|
||||
if pr.state == "open":
|
||||
logging.debug(f"PR is open: {pr.number}")
|
||||
label_strs = set([label.name for label in pr.get_labels()])
|
||||
@@ -67,19 +72,31 @@ if __name__ == "__main__":
|
||||
for lang in langs:
|
||||
if lang in translations_map:
|
||||
num = translations_map[lang]
|
||||
logging.info(f"Found a translation issue for language: {lang} in issue: {num}")
|
||||
logging.info(
|
||||
f"Found a translation issue for language: {lang} in issue: {num}"
|
||||
)
|
||||
issue = repo.get_issue(num)
|
||||
message = f"Good news everyone! 😉 There's a new translation PR to be reviewed: #{pr.number} 🎉"
|
||||
already_notified = False
|
||||
logging.info(f"Checking current comments in issue: {num} to see if already notified about this PR: {pr.number}")
|
||||
time.sleep(sleep_time)
|
||||
logging.info(
|
||||
f"Sleeping for {sleep_time} seconds to avoid race conditions and multiple comments"
|
||||
)
|
||||
logging.info(
|
||||
f"Checking current comments in issue: {num} to see if already notified about this PR: {pr.number}"
|
||||
)
|
||||
for comment in issue.get_comments():
|
||||
if message in comment.body:
|
||||
already_notified = True
|
||||
if not already_notified:
|
||||
logging.info(f"Writing comment in issue: {num} about PR: {pr.number}")
|
||||
logging.info(
|
||||
f"Writing comment in issue: {num} about PR: {pr.number}"
|
||||
)
|
||||
issue.create_comment(message)
|
||||
else:
|
||||
logging.info(f"Issue: {num} was already notified of PR: {pr.number}")
|
||||
logging.info(
|
||||
f"Issue: {num} was already notified of PR: {pr.number}"
|
||||
)
|
||||
else:
|
||||
logging.info(
|
||||
f"Changing labels in a closed PR doesn't trigger comments, PR: {pr.number}"
|
||||
|
||||
@@ -12,3 +12,4 @@ sq: 2041
|
||||
pl: 3169
|
||||
de: 3716
|
||||
id: 3717
|
||||
az: 3994
|
||||
|
||||
7
.github/workflows/test.yml
vendored
@@ -2,6 +2,8 @@ name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
@@ -10,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
@@ -30,6 +32,9 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: flit install --symlink
|
||||
- name: Lint
|
||||
if: ${{ matrix.python-version != '3.6' }}
|
||||
run: bash scripts/lint.sh
|
||||
- name: Test
|
||||
run: bash scripts/test.sh
|
||||
- name: Upload coverage
|
||||
|
||||
21
README.md
@@ -5,8 +5,8 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
|
||||
@@ -14,6 +14,9 @@
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -45,11 +48,13 @@ The key features are:
|
||||
<!-- sponsors -->
|
||||
|
||||
<a href="https://bit.ly/2QSouzH" target="_blank" title="Jina: build neural search-as-a-service for any kind of data in just minutes."><img src="https://fastapi.tiangolo.com/img/sponsors/jina.svg"></a>
|
||||
<a href="https://cryptapi.io/" target="_blank" title="CryptAPI: Your easy to use, secure and privacy oriented payment gateway."><img src="https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg"></a>
|
||||
<a href="https://www.deta.sh/?ref=fastapi" target="_blank" title="The launchpad for all your (team's) ideas"><img src="https://fastapi.tiangolo.com/img/sponsors/deta.svg"></a>
|
||||
<a href="https://www.investsuite.com/jobs" target="_blank" title="Wealthtech jobs with FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/investsuite.svg"></a>
|
||||
<a href="https://www.vim.so/?utm_source=FastAPI" target="_blank" title="We help you master vim with interactive exercises"><img src="https://fastapi.tiangolo.com/img/sponsors/vimso.png"></a>
|
||||
<a href="https://talkpython.fm/fastapi-sponsor" target="_blank" title="FastAPI video courses on demand from people you trust"><img src="https://fastapi.tiangolo.com/img/sponsors/talkpython.png"></a>
|
||||
<a href="https://testdriven.io/courses/tdd-fastapi/" target="_blank" title="Learn to build high-quality web apps with best practices"><img src="https://fastapi.tiangolo.com/img/sponsors/testdriven.svg"></a>
|
||||
<a href="https://github.com/deepset-ai/haystack/" target="_blank" title="Build powerful search from composable, open source building blocks"><img src="https://fastapi.tiangolo.com/img/sponsors/haystack-fastapi.svg"></a>
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
@@ -129,7 +134,7 @@ You will also need an ASGI server, for production such as <a href="https://www.u
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install uvicorn[standard]
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
@@ -315,7 +320,7 @@ And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" targe
|
||||
|
||||
### Recap
|
||||
|
||||
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
|
||||
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
|
||||
|
||||
You do that with standard modern Python types.
|
||||
|
||||
@@ -372,7 +377,7 @@ Coming back to the previous code example, **FastAPI** will:
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* 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 it has a required attribute `name` that should be a `str`.
|
||||
* 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.
|
||||
@@ -417,9 +422,9 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
|
||||
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
@@ -441,12 +446,10 @@ Used by Pydantic:
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Required if you want to use `FileResponse` or `StaticFiles`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
|
||||
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
|
||||
|
||||
Used by FastAPI / Starlette:
|
||||
@@ -454,7 +457,7 @@ Used by FastAPI / Starlette:
|
||||
* <a href="https://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]`.
|
||||
You can install all of these with `pip install "fastapi[all]"`.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
129
docs/az/mkdocs.yml
Normal file
@@ -0,0 +1,129 @@
|
||||
site_name: FastAPI
|
||||
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
||||
site_url: https://fastapi.tiangolo.com/az/
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: overrides
|
||||
palette:
|
||||
- scheme: default
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb
|
||||
name: Switch to light mode
|
||||
- scheme: slate
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb-outline
|
||||
name: Switch to dark mode
|
||||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
favicon: https://fastapi.tiangolo.com/img/favicon.png
|
||||
language: en
|
||||
repo_name: tiangolo/fastapi
|
||||
repo_url: https://github.com/tiangolo/fastapi
|
||||
edit_uri: ''
|
||||
plugins:
|
||||
- search
|
||||
- markdownextradata:
|
||||
data: data
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- az: /az/
|
||||
- de: /de/
|
||||
- es: /es/
|
||||
- fr: /fr/
|
||||
- id: /id/
|
||||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- sq: /sq/
|
||||
- tr: /tr/
|
||||
- uk: /uk/
|
||||
- zh: /zh/
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
- markdown.extensions.codehilite:
|
||||
guess_lang: false
|
||||
- mdx_include:
|
||||
base_path: docs
|
||||
- admonition
|
||||
- codehilite
|
||||
- extra
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format ''
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
extra:
|
||||
analytics:
|
||||
provider: google
|
||||
property: UA-133183413-1
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/fastapi
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
link: https://dev.to/tiangolo
|
||||
- icon: fontawesome/brands/medium
|
||||
link: https://medium.com/@tiangolo
|
||||
- icon: fontawesome/solid/globe
|
||||
link: https://tiangolo.com
|
||||
alternate:
|
||||
- link: /
|
||||
name: en - English
|
||||
- link: /az/
|
||||
name: az
|
||||
- link: /de/
|
||||
name: de
|
||||
- link: /es/
|
||||
name: es - español
|
||||
- link: /fr/
|
||||
name: fr - français
|
||||
- link: /id/
|
||||
name: id
|
||||
- link: /it/
|
||||
name: it - italiano
|
||||
- link: /ja/
|
||||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /pl/
|
||||
name: pl
|
||||
- link: /pt/
|
||||
name: pt - português
|
||||
- link: /ru/
|
||||
name: ru - русский язык
|
||||
- link: /sq/
|
||||
name: sq - shqip
|
||||
- link: /tr/
|
||||
name: tr - Türkçe
|
||||
- link: /uk/
|
||||
name: uk - українська мова
|
||||
- link: /zh/
|
||||
name: zh - 汉语
|
||||
extra_css:
|
||||
- https://fastapi.tiangolo.com/css/termynal.css
|
||||
- https://fastapi.tiangolo.com/css/custom.css
|
||||
extra_javascript:
|
||||
- https://fastapi.tiangolo.com/js/termynal.js
|
||||
- https://fastapi.tiangolo.com/js/custom.js
|
||||
0
docs/az/overrides/.gitignore
vendored
Normal file
203
docs/de/docs/features.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Merkmale
|
||||
|
||||
## FastAPI Merkmale
|
||||
|
||||
**FastAPI** ermöglicht Ihnen folgendes:
|
||||
|
||||
### Basiert auf offenen Standards
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> für API-Erstellung, zusammen mit Deklarationen von <abbr title="auch genannt: Endpunkte, Routen">Pfad</abbr> <abbr title="gemeint sind: HTTP-Methoden, wie POST, GET, PUT, DELETE">Operationen</abbr>, Parameter, Nachrichtenrumpf-Anfragen (englisch: body request), Sicherheit, etc.
|
||||
* Automatische Dokumentation der Datenentitäten mit dem <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (OpenAPI basiert selber auf dem JSON Schema).
|
||||
* Entworfen auf Grundlage dieser Standards nach einer sorgfältigen Studie, statt einer nachträglichen Schicht über diesen Standards.
|
||||
* Dies ermöglicht automatische **Quellcode-Generierung auf Benutzerebene** in vielen Sprachen.
|
||||
|
||||
### Automatische Dokumentation
|
||||
|
||||
Mit einer interaktiven API-Dokumentation und explorativen webbasierten Benutzerschnittstellen. Da FastAPI auf OpenAPI basiert, gibt es hierzu mehrere Optionen, wobei zwei standartmäßig vorhanden sind.
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, bietet interaktive Exploration: testen und rufen Sie ihre API direkt vom Webbrowser auf.
|
||||
|
||||

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

|
||||
|
||||
### Nur modernes Python
|
||||
|
||||
Alles basiert auf **Python 3.6 Typ**-Deklarationen (dank Pydantic). Es muss keine neue Syntax gelernt werden, nur standardisiertes modernes Python.
|
||||
|
||||
|
||||
|
||||
Wenn Sie eine kurze, zweiminütige, Auffrischung in der Benutzung von Python Typ-Deklarationen benötigen (auch wenn Sie FastAPI nicht nutzen), schauen Sie sich diese kurze Einführung an (Englisch): Python Types{.internal-link target=_blank}.
|
||||
|
||||
Sie schreiben Standard-Python mit Typ-Deklarationen:
|
||||
|
||||
```Python
|
||||
from typing import List, Dict
|
||||
from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Deklariere eine Variable als str
|
||||
# und bekomme Editor-Unterstütung innerhalb der Funktion
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
|
||||
# Ein Pydantic model
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
joined: date
|
||||
```
|
||||
|
||||
Dies kann nun wiefolgt benutzt werden:
|
||||
|
||||
```Python
|
||||
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
|
||||
|
||||
second_user_data = {
|
||||
"id": 4,
|
||||
"name": "Mary",
|
||||
"joined": "2018-11-30",
|
||||
}
|
||||
|
||||
my_second_user: User = User(**second_user_data)
|
||||
```
|
||||
|
||||
!!! info
|
||||
`**second_user_data` bedeutet:
|
||||
|
||||
Übergebe die Schlüssel und die zugehörigen Werte des `second_user_data` Datenwörterbuches direkt als Schlüssel-Wert Argumente, äquivalent zu: `User(id=4, name="Mary", joined="2018-11-30")`
|
||||
|
||||
### Editor Unterstützung
|
||||
|
||||
FastAPI wurde so entworfen, dass es einfach und intuitiv zu benutzen ist; alle Entscheidungen wurden auf mehreren Editoren getestet (sogar vor der eigentlichen Implementierung), um so eine best mögliche Entwicklererfahrung zu gewährleisten.
|
||||
|
||||
In der letzen Python Entwickler Umfrage stellte sich heraus, dass <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">die meist genutzte Funktion die "Autovervollständigung" ist</a>.
|
||||
|
||||
Die gesamte Struktur von **FastAPI** soll dem gerecht werden. Autovervollständigung funktioniert überall.
|
||||
|
||||
Sie müssen selten in die Dokumentation schauen.
|
||||
|
||||
So kann ihr Editor Sie unterstützen:
|
||||
|
||||
* 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>:
|
||||
|
||||

|
||||
|
||||
Sie bekommen Autovervollständigung an Stellen, an denen Sie dies vorher nicht für möglich gehalten hätten. Zum Beispiel der `price` Schlüssel aus einem JSON Datensatz (dieser könnte auch verschachtelt sein) aus einer Anfrage.
|
||||
|
||||
Hierdurch werden Sie nie wieder einen falschen Schlüsselnamen benutzen und sparen sich lästiges Suchen in der Dokumentation, um beispielsweise herauszufinden ob Sie `username` oder `user_name` als Schlüssel verwenden.
|
||||
|
||||
### Kompakt
|
||||
|
||||
FastAPI nutzt für alles sensible **Standard-Einstellungen**, welche optional überall konfiguriert werden können. Alle Parameter können ganz genau an Ihre Bedürfnisse angepasst werden, sodass sie genau die API definieren können, die sie brachen.
|
||||
|
||||
Aber standartmäßig, **"funktioniert einfach"** alles.
|
||||
|
||||
### Validierung
|
||||
|
||||
* Validierung für die meisten (oder alle?) Python **Datentypen**, hierzu gehören:
|
||||
* JSON Objekte (`dict`).
|
||||
* JSON Listen (`list`), die den Typ ihrer Elemente definieren.
|
||||
* Zeichenketten (`str`), mit definierter minimaler und maximaler Länge.
|
||||
* Zahlen (`int`, `float`) mit minimaler und maximaler Größe, usw.
|
||||
|
||||
* Validierung für ungewögnliche Typen, wie:
|
||||
* URL.
|
||||
* Email.
|
||||
* UUID.
|
||||
* ... und andere.
|
||||
|
||||
Die gesamte Validierung übernimmt das etablierte und robuste **Pydantic**.
|
||||
|
||||
### Sicherheit und Authentifizierung
|
||||
|
||||
Sicherheit und Authentifizierung integriert. Ohne einen Kompromiss aufgrund einer Datenbank oder den Datenentitäten.
|
||||
|
||||
Unterstützt alle von OpenAPI definierten Sicherheitsschemata, hierzu gehören:
|
||||
|
||||
* HTTP Basis Authentifizierung.
|
||||
* **OAuth2** (auch mit **JWT Zugriffstokens**). Schauen Sie sich hierzu dieses Tutorial an: [OAuth2 mit JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
|
||||
* API Schlüssel in:
|
||||
* Kopfzeile (HTTP Header).
|
||||
* Anfrageparametern.
|
||||
* Cookies, etc.
|
||||
|
||||
Zusätzlich gibt es alle Sicherheitsfunktionen von Starlette (auch **session cookies**).
|
||||
|
||||
Alles wurde als wiederverwendbare Werkzeuge und Komponenten geschaffen, die einfach in ihre Systeme, Datenablagen, relationale und nicht-relationale Datenbanken, ..., integriert werden können.
|
||||
|
||||
### Einbringen von Abhängigkeiten (meist: Dependency Injection)
|
||||
|
||||
FastAPI enthält ein extrem einfaches, aber extrem mächtiges <abbr title='oft verwendet im Zusammenhang von: Komponenten, Resourcen, Diensten, Dienstanbieter'><strong>Dependency Injection</strong></abbr> System.
|
||||
|
||||
* Selbst Abhängigkeiten können Abhängigkeiten haben, woraus eine Hierachie oder ein **"Graph" von Abhängigkeiten** entsteht.
|
||||
* **Automatische Umsetzung** durch FastAPI.
|
||||
* Alle abhängigen Komponenten könnten Daten von Anfragen, **Erweiterungen der Pfadoperations-**Einschränkungen und der automatisierten Dokumentation benötigen.
|
||||
* **Automatische Validierung** selbst für *Pfadoperationen*-Parameter, die in den Abhängigkeiten definiert wurden.
|
||||
* Unterstütz komplexe Benutzerauthentifizierungssysteme, mit **Datenbankverbindungen**, usw.
|
||||
* **Keine Kompromisse** bei Datenbanken, Eingabemasken, usw. Sondern einfache Integration von allen.
|
||||
|
||||
### Unbegrenzte Erweiterungen
|
||||
|
||||
Oder mit anderen Worten, sie werden nicht benötigt. Importieren und nutzen Sie Quellcode nach Bedarf.
|
||||
|
||||
Jede Integration wurde so entworfen, dass sie einfach zu nutzen ist (mit Abhängigkeiten), sodass Sie eine Erweiterung für Ihre Anwendung mit nur zwei Zeilen an Quellcode implementieren können. Hierbei nutzen Sie die selbe Struktur und Syntax, wie bei Pfadoperationen.
|
||||
|
||||
### Getestet
|
||||
|
||||
* 100% <abbr title="Die Anzahl an Code, die automatisch getestet wird">Testabdeckung</abbr>.
|
||||
* 100% <abbr title="Python Typ Annotationen, mit dennen Ihr Editor und andere exteren Werkezuge Sie besser unterstützen können">Typen annotiert</abbr>.
|
||||
* Verwendet in Produktionsanwendungen.
|
||||
|
||||
## Starlette's Merkmale
|
||||
|
||||
**FastAPI** ist vollkommen kompatibel (und basiert auf) <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Das bedeutet, auch ihr eigner Starlett Quellcode funktioniert.
|
||||
|
||||
`FastAPI` ist eigentlich eine Unterklasse von `Starlette`. Wenn sie also bereits Starlette kennen oder benutzen, können Sie das meiste Ihres Wissen direkt anwenden.
|
||||
|
||||
Mit **FastAPI** bekommen Sie viele von **Starlette**'s Funktionen (da FastAPI nur Starlette auf Steroiden ist):
|
||||
|
||||
* Stark beeindruckende Performanz. Es ist <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">eines der schnellsten Python frameworks, auf Augenhöhe mit **NodeJS** und **Go**</a>.
|
||||
* **WebSocket**-Unterstützung.
|
||||
* Hintergrundaufgaben im selben Prozess.
|
||||
* Ereignisse für das Starten und Herunterfahren.
|
||||
* Testclient basierend auf `requests`.
|
||||
* **CORS**, GZip, statische Dateien, Antwortfluss.
|
||||
* **Sitzungs und Cookie** Unterstützung.
|
||||
* 100% Testabdeckung.
|
||||
* 100% Typen annotiert.
|
||||
|
||||
## Pydantic's Merkmale
|
||||
|
||||
**FastAPI** ist vollkommen kompatibel (und basiert auf) <a href="https://pydantic-docs.helpmanual.io" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Das bedeutet, auch jeder zusätzliche Pydantic Quellcode funktioniert.
|
||||
|
||||
Verfügbar sind ebenso externe auf Pydantic basierende Bibliotheken, wie <abbr title="Object-Relational Mapper (Abbildung von Objekten auf relationale Strukturen)">ORM</abbr>s, <abbr title="Object-Document Mapper (Abbildung von Objekten auf nicht-relationale Strukturen)">ODM</abbr>s für Datenbanken.
|
||||
|
||||
Daher können Sie in vielen Fällen das Objekt einer Anfrage **direkt zur Datenbank** schicken, weil alles automatisch validiert wird.
|
||||
|
||||
Das selbe gilt auch für die andere Richtung: Sie können jedes Objekt aus der Datenbank **direkt zum Klienten** schicken.
|
||||
|
||||
Mit **FastAPI** bekommen Sie alle Funktionen von **Pydantic** (da FastAPI für die gesamte Datenverarbeitung Pydantic nutzt):
|
||||
|
||||
* **Kein Kopfzerbrechen**:
|
||||
* Sie müssen keine neue Schemadefinitionssprache lernen.
|
||||
* Wenn Sie mit Python's Typisierung arbeiten können, können Sie auch mit Pydantic arbeiten.
|
||||
* Gutes Zusammenspiel mit Ihrer/Ihrem **<abbr title="Integrierten Entwicklungsumgebung, ähnlich zu (Quellcode-)Editor">IDE</abbr>/<abbr title="Ein Programm, was Fehler im Quellcode sucht">linter</abbr>/Gehirn**:
|
||||
* Weil Datenstrukturen von Pydantic einfach nur Instanzen ihrer definierten Klassen sind, sollten Autovervollständigung, Linting, mypy und ihre Intuition einwandfrei funktionieren.
|
||||
* **Schnell**:
|
||||
* In <a href="https://pydantic-docs.helpmanual.io/#benchmarks-tag" class="external-link" target="_blank">Vergleichen</a> ist Pydantic schneller als jede andere getestete Bibliothek.
|
||||
* Validierung von **komplexen Strukturen**:
|
||||
* Benutzung von hierachischen Pydantic Schemata, Python `typing`’s `List` und `Dict`, etc.
|
||||
* Validierungen erlauben klare und einfache Datenschemadefinition, überprüft und dokumentiert als JSON Schema.
|
||||
* Sie können stark **verschachtelte JSON** Objekte haben und diese sind trotzdem validiert und annotiert.
|
||||
* **Erweiterbar**:
|
||||
* Pydantic erlaubt die Definition von eigenen Datentypen oder sie können die Validierung mit einer `validator` dekorierten Methode erweitern..
|
||||
* 100% Testabdeckung.
|
||||
@@ -425,7 +425,6 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
@@ -452,7 +451,6 @@ Used by Starlette:
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
|
||||
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
|
||||
|
||||
Used by FastAPI / Starlette:
|
||||
|
||||
@@ -9,13 +9,13 @@ theme:
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb-outline
|
||||
icon: material/lightbulb
|
||||
name: Switch to light mode
|
||||
- scheme: slate
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb
|
||||
icon: material/lightbulb-outline
|
||||
name: Switch to dark mode
|
||||
features:
|
||||
- search.suggest
|
||||
@@ -29,9 +29,6 @@ theme:
|
||||
repo_name: tiangolo/fastapi
|
||||
repo_url: https://github.com/tiangolo/fastapi
|
||||
edit_uri: ''
|
||||
google_analytics:
|
||||
- UA-133183413-1
|
||||
- auto
|
||||
plugins:
|
||||
- search
|
||||
- markdownextradata:
|
||||
@@ -40,6 +37,7 @@ nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- az: /az/
|
||||
- de: /de/
|
||||
- es: /es/
|
||||
- fr: /fr/
|
||||
@@ -54,6 +52,7 @@ nav:
|
||||
- tr: /tr/
|
||||
- uk: /uk/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
@@ -69,8 +68,12 @@ markdown_extensions:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format ''
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
extra:
|
||||
analytics:
|
||||
provider: google
|
||||
property: UA-133183413-1
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/fastapi
|
||||
@@ -89,6 +92,8 @@ extra:
|
||||
alternate:
|
||||
- link: /
|
||||
name: en - English
|
||||
- link: /az/
|
||||
name: az
|
||||
- link: /de/
|
||||
name: de
|
||||
- link: /es/
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
articles:
|
||||
english:
|
||||
- author: Kaustubh Gupta
|
||||
author_link: https://medium.com/@kaustubhgupta1828/
|
||||
link: https://www.analyticsvidhya.com/blog/2021/06/deploying-ml-models-as-api-using-fastapi-and-heroku/
|
||||
title: Deploying ML Models as API Using FastAPI and Heroku
|
||||
- link: https://jarmos.netlify.app/posts/using-github-actions-to-deploy-a-fastapi-project-to-heroku/
|
||||
title: Using GitHub Actions to Deploy a FastAPI Project to Heroku
|
||||
author_link: https://jarmos.netlify.app/
|
||||
author: Somraj Saha
|
||||
- author: "@pystar"
|
||||
author_link: https://pystar.substack.com/
|
||||
link: https://pystar.substack.com/p/how-to-create-a-fake-certificate
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
maintainers:
|
||||
- login: tiangolo
|
||||
answers: 1229
|
||||
prs: 250
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=05f95ca7fdead36edd9c86be46b4ef6c3c71f876&v=4
|
||||
answers: 1230
|
||||
prs: 269
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=5cad72c846b7aba2e960546af490edc7375dafc4&v=4
|
||||
url: https://github.com/tiangolo
|
||||
experts:
|
||||
- login: Kludex
|
||||
count: 278
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
|
||||
count: 316
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: dmontagu
|
||||
count: 262
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
|
||||
url: https://github.com/dmontagu
|
||||
- login: ycd
|
||||
count: 217
|
||||
count: 219
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: Mause
|
||||
count: 202
|
||||
count: 207
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
|
||||
url: https://github.com/Mause
|
||||
- login: euri10
|
||||
@@ -30,69 +30,85 @@ experts:
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
|
||||
url: https://github.com/phy25
|
||||
- login: ArcLightSlavik
|
||||
count: 66
|
||||
count: 71
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
|
||||
url: https://github.com/ArcLightSlavik
|
||||
- login: raphaelauv
|
||||
count: 67
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
|
||||
url: https://github.com/raphaelauv
|
||||
- login: falkben
|
||||
count: 57
|
||||
count: 58
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4
|
||||
url: https://github.com/falkben
|
||||
- login: sm-Fifteen
|
||||
count: 48
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
|
||||
url: https://github.com/sm-Fifteen
|
||||
- login: raphaelauv
|
||||
count: 47
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
|
||||
url: https://github.com/raphaelauv
|
||||
- login: insomnes
|
||||
count: 46
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
|
||||
url: https://github.com/insomnes
|
||||
- login: Dustyposa
|
||||
count: 42
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
|
||||
url: https://github.com/Dustyposa
|
||||
- login: includeamin
|
||||
count: 38
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
|
||||
url: https://github.com/includeamin
|
||||
- login: Dustyposa
|
||||
count: 38
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
|
||||
url: https://github.com/Dustyposa
|
||||
- login: prostomarkeloff
|
||||
count: 33
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
|
||||
url: https://github.com/prostomarkeloff
|
||||
- login: STeveShary
|
||||
count: 32
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
|
||||
url: https://github.com/STeveShary
|
||||
- login: krishnardt
|
||||
count: 31
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
|
||||
url: https://github.com/krishnardt
|
||||
- login: insomnes
|
||||
count: 30
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
|
||||
url: https://github.com/insomnes
|
||||
- login: wshayes
|
||||
count: 29
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
|
||||
url: https://github.com/wshayes
|
||||
- login: frankie567
|
||||
count: 29
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
|
||||
url: https://github.com/frankie567
|
||||
- login: adriangb
|
||||
count: 28
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4
|
||||
url: https://github.com/adriangb
|
||||
- login: ghandic
|
||||
count: 25
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
|
||||
url: https://github.com/ghandic
|
||||
- login: dbanty
|
||||
count: 25
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9bcce836bbce55835291c5b2ac93a4e311f4b3c3&v=4
|
||||
url: https://github.com/dbanty
|
||||
- login: chbndrhnns
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
|
||||
url: https://github.com/chbndrhnns
|
||||
- login: SirTelemak
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
|
||||
url: https://github.com/SirTelemak
|
||||
- login: chbndrhnns
|
||||
- login: panla
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
|
||||
url: https://github.com/chbndrhnns
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
|
||||
url: https://github.com/panla
|
||||
- login: acnebs
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=bfd127b3e6200f4d00afd714f0fc95c2512df19b&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4
|
||||
url: https://github.com/acnebs
|
||||
- login: nsidnev
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
|
||||
url: https://github.com/nsidnev
|
||||
- login: frankie567
|
||||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
|
||||
url: https://github.com/frankie567
|
||||
- login: chris-allnutt
|
||||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
|
||||
@@ -117,34 +133,30 @@ experts:
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
|
||||
url: https://github.com/waynerv
|
||||
- login: adriangb
|
||||
- login: acidjunk
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
|
||||
url: https://github.com/acidjunk
|
||||
- login: dstlny
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4
|
||||
url: https://github.com/adriangb
|
||||
- login: panla
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
|
||||
url: https://github.com/panla
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
|
||||
url: https://github.com/dstlny
|
||||
- login: haizaar
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/58201?u=4f1f9843d69433ca0d380d95146cfe119e5fdac4&v=4
|
||||
url: https://github.com/haizaar
|
||||
- login: STeveShary
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
|
||||
url: https://github.com/STeveShary
|
||||
- login: acidjunk
|
||||
- login: David-Lor
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/17401854?u=474680c02b94cba810cb9032fb7eb787d9cc9d22&v=4
|
||||
url: https://github.com/David-Lor
|
||||
- login: lowercase00
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
|
||||
url: https://github.com/acidjunk
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21188280?v=4
|
||||
url: https://github.com/lowercase00
|
||||
- login: zamiramir
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/40475662?u=e58ef61034e8d0d6a312cc956fb09b9c3332b449&v=4
|
||||
url: https://github.com/zamiramir
|
||||
- login: David-Lor
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/17401854?u=474680c02b94cba810cb9032fb7eb787d9cc9d22&v=4
|
||||
url: https://github.com/David-Lor
|
||||
- login: juntatalor
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8134632?v=4
|
||||
@@ -165,43 +177,35 @@ experts:
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/47581948?v=4
|
||||
url: https://github.com/hellocoldworld
|
||||
last_month_active:
|
||||
- login: Mause
|
||||
- login: oligond
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
|
||||
url: https://github.com/Mause
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2858306?u=1bb1182a5944e93624b7fb26585f22c8f7a9d76e&v=4
|
||||
url: https://github.com/oligond
|
||||
last_month_active:
|
||||
- login: insomnes
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
|
||||
url: https://github.com/insomnes
|
||||
- login: raphaelauv
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
|
||||
url: https://github.com/raphaelauv
|
||||
- login: jgould22
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: harunyasar
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
|
||||
url: https://github.com/harunyasar
|
||||
- login: Kludex
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: panla
|
||||
count: 7
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
|
||||
url: https://github.com/panla
|
||||
- login: adriangb
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4
|
||||
url: https://github.com/adriangb
|
||||
- login: Dustyposa
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
|
||||
url: https://github.com/Dustyposa
|
||||
- login: STeveShary
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
|
||||
url: https://github.com/STeveShary
|
||||
- login: Cosmicoppai
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/63765823?u=b97c98ddd6eaf06cd5a0d312db36f97996ac5b23&v=4
|
||||
url: https://github.com/Cosmicoppai
|
||||
- login: AlexanderPodorov
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/54511144?v=4
|
||||
url: https://github.com/AlexanderPodorov
|
||||
- login: SamuelHeath
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/12291364?v=4
|
||||
url: https://github.com/SamuelHeath
|
||||
top_contributors:
|
||||
- login: waynerv
|
||||
count: 25
|
||||
@@ -219,29 +223,33 @@ top_contributors:
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
|
||||
url: https://github.com/euri10
|
||||
- login: jaystone776
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
|
||||
url: https://github.com/jaystone776
|
||||
- login: mariacamilagl
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
|
||||
url: https://github.com/mariacamilagl
|
||||
- login: jaystone776
|
||||
- login: Smlep
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
|
||||
url: https://github.com/jaystone776
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
|
||||
url: https://github.com/Smlep
|
||||
- login: Serrones
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
|
||||
url: https://github.com/Serrones
|
||||
- login: RunningIkkyu
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=706e1ee3f248245f2d68b976d149d06fd5a2010d&v=4
|
||||
url: https://github.com/RunningIkkyu
|
||||
- login: Serrones
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
|
||||
url: https://github.com/Serrones
|
||||
- login: Kludex
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: hard-coders
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
|
||||
url: https://github.com/hard-coders
|
||||
- login: wshayes
|
||||
count: 5
|
||||
@@ -263,19 +271,27 @@ top_contributors:
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
|
||||
url: https://github.com/jfunez
|
||||
- login: ycd
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: komtaki
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
|
||||
url: https://github.com/komtaki
|
||||
- login: Smlep
|
||||
- login: NinaHwang
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
|
||||
url: https://github.com/Smlep
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=1741703bd6c8f491503354b363a86e879b4c1cab&v=4
|
||||
url: https://github.com/NinaHwang
|
||||
top_reviewers:
|
||||
- login: Kludex
|
||||
count: 85
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
|
||||
count: 91
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: waynerv
|
||||
count: 47
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
|
||||
url: https://github.com/waynerv
|
||||
- login: Laineyzhang55
|
||||
count: 47
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
|
||||
@@ -284,34 +300,38 @@ top_reviewers:
|
||||
count: 46
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
|
||||
url: https://github.com/tokusumi
|
||||
- login: waynerv
|
||||
count: 43
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
|
||||
url: https://github.com/waynerv
|
||||
- login: ycd
|
||||
count: 41
|
||||
count: 44
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: AdrianDeAnda
|
||||
count: 28
|
||||
count: 33
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=bb7f8a0d6c9de4e9d0320a9f271210206e202250&v=4
|
||||
url: https://github.com/AdrianDeAnda
|
||||
- login: ArcLightSlavik
|
||||
count: 31
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
|
||||
url: https://github.com/ArcLightSlavik
|
||||
- login: dmontagu
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
|
||||
url: https://github.com/dmontagu
|
||||
- login: cassiobotaro
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4
|
||||
url: https://github.com/cassiobotaro
|
||||
- login: komtaki
|
||||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
|
||||
url: https://github.com/komtaki
|
||||
- login: ArcLightSlavik
|
||||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
|
||||
url: https://github.com/ArcLightSlavik
|
||||
- login: cassiobotaro
|
||||
count: 18
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4
|
||||
url: https://github.com/cassiobotaro
|
||||
- login: hard-coders
|
||||
count: 19
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
|
||||
url: https://github.com/hard-coders
|
||||
- login: 0417taehyun
|
||||
count: 19
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
|
||||
url: https://github.com/0417taehyun
|
||||
- login: yanever
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21978760?v=4
|
||||
@@ -320,6 +340,14 @@ top_reviewers:
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
|
||||
url: https://github.com/SwftAlpc
|
||||
- login: Smlep
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
|
||||
url: https://github.com/Smlep
|
||||
- login: DevDae
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
|
||||
url: https://github.com/DevDae
|
||||
- login: pedabraham
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
|
||||
@@ -329,9 +357,13 @@ top_reviewers:
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
|
||||
url: https://github.com/delhi09
|
||||
- login: lsglucas
|
||||
count: 13
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
|
||||
url: https://github.com/lsglucas
|
||||
- login: rjNemo
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
|
||||
url: https://github.com/rjNemo
|
||||
- login: RunningIkkyu
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=706e1ee3f248245f2d68b976d149d06fd5a2010d&v=4
|
||||
@@ -340,14 +372,10 @@ top_reviewers:
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
|
||||
url: https://github.com/sh0nk
|
||||
- login: rjNemo
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
|
||||
url: https://github.com/rjNemo
|
||||
- login: hard-coders
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
|
||||
url: https://github.com/hard-coders
|
||||
- login: yezz123
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
|
||||
url: https://github.com/yezz123
|
||||
- login: mariacamilagl
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
|
||||
@@ -360,22 +388,34 @@ top_reviewers:
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
|
||||
url: https://github.com/maoyibo
|
||||
- login: Smlep
|
||||
- login: graingert
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
|
||||
url: https://github.com/Smlep
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/413772?v=4
|
||||
url: https://github.com/graingert
|
||||
- login: PandaHun
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13096845?u=646eba44db720e37d0dbe8e98e77ab534ea78a20&v=4
|
||||
url: https://github.com/PandaHun
|
||||
- login: kty4119
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/49435654?v=4
|
||||
url: https://github.com/kty4119
|
||||
- login: bezaca
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
|
||||
url: https://github.com/bezaca
|
||||
- login: solomein-sv
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=46acfb4aeefb1d7b9fdc5a8cbd9eb8744683c47a&v=4
|
||||
url: https://github.com/solomein-sv
|
||||
- login: blt232018
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4
|
||||
url: https://github.com/blt232018
|
||||
- login: ComicShrimp
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=b3e4d9a14d9a65d429ce62c566aef73178b7111d&v=4
|
||||
url: https://github.com/ComicShrimp
|
||||
- login: Serrones
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
|
||||
@@ -388,10 +428,10 @@ top_reviewers:
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
|
||||
url: https://github.com/raphaelauv
|
||||
- login: graingert
|
||||
- login: BilalAlpaslan
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/413772?v=4
|
||||
url: https://github.com/graingert
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
|
||||
url: https://github.com/BilalAlpaslan
|
||||
- login: NastasiaSaby
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8245071?u=b3afd005f9e4bf080c219ef61a592b3a8004b764&v=4
|
||||
@@ -400,14 +440,26 @@ top_reviewers:
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
|
||||
url: https://github.com/Mause
|
||||
- login: krocdort
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34248814?v=4
|
||||
url: https://github.com/krocdort
|
||||
- login: jovicon
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4
|
||||
url: https://github.com/jovicon
|
||||
- login: diogoduartec
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31852339?u=b50fc11c531e9b77922e19edfc9e7233d4d7b92e&v=4
|
||||
url: https://github.com/diogoduartec
|
||||
- login: nimctl
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=e39b11d47188744ee07b2a1c7ce1a1bdf3c80760&v=4
|
||||
url: https://github.com/nimctl
|
||||
- login: israteneda
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20668624?u=d7b2961d330aca65fbce5bdb26a0800a3d23ed2d&v=4
|
||||
url: https://github.com/israteneda
|
||||
- login: juntatalor
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8134632?v=4
|
||||
@@ -416,43 +468,15 @@ top_reviewers:
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/63564282?u=0078826509dbecb2fdb543f4e881c9cd06157893&v=4
|
||||
url: https://github.com/SnkSynthesis
|
||||
- login: solomein-sv
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=46acfb4aeefb1d7b9fdc5a8cbd9eb8744683c47a&v=4
|
||||
url: https://github.com/solomein-sv
|
||||
- login: anthonycepeda
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4
|
||||
url: https://github.com/anthonycepeda
|
||||
- login: ComicShrimp
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=b3e4d9a14d9a65d429ce62c566aef73178b7111d&v=4
|
||||
url: https://github.com/ComicShrimp
|
||||
- login: oandersonmagalhaes
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4
|
||||
url: https://github.com/oandersonmagalhaes
|
||||
- login: euri10
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
|
||||
url: https://github.com/euri10
|
||||
- login: rkbeatss
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/23391143?u=56ab6bff50be950fa8cae5cf736f2ae66e319ff3&v=4
|
||||
url: https://github.com/rkbeatss
|
||||
- login: aviramha
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41201924?u=6883cc4fc13a7b2e60d4deddd4be06f9c5287880&v=4
|
||||
url: https://github.com/aviramha
|
||||
- login: Cajuteq
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/26676532?u=8ee0422981810e51480855de1c0d67b6b79cd3f2&v=4
|
||||
url: https://github.com/Cajuteq
|
||||
- login: Zxilly
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31370133?v=4
|
||||
url: https://github.com/Zxilly
|
||||
- login: Bluenix2
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/38372706?u=c9d28aff15958d6ebf1971148bfb3154ff943c4f&v=4
|
||||
url: https://github.com/Bluenix2
|
||||
- login: qysfblog
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/52229895?v=4
|
||||
url: https://github.com/qysfblog
|
||||
|
||||
@@ -2,6 +2,9 @@ gold:
|
||||
- url: https://bit.ly/2QSouzH
|
||||
title: "Jina: build neural search-as-a-service for any kind of data in just minutes."
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/jina.svg
|
||||
- url: https://cryptapi.io/
|
||||
title: "CryptAPI: Your easy to use, secure and privacy oriented payment gateway."
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg
|
||||
silver:
|
||||
- url: https://www.deta.sh/?ref=fastapi
|
||||
title: The launchpad for all your (team's) ideas
|
||||
@@ -18,3 +21,10 @@ silver:
|
||||
- url: https://testdriven.io/courses/tdd-fastapi/
|
||||
title: Learn to build high-quality web apps with best practices
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg
|
||||
- url: https://github.com/deepset-ai/haystack/
|
||||
title: Build powerful search from composable, open source building blocks
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/haystack-fastapi.svg
|
||||
bronze:
|
||||
- url: https://calmcode.io
|
||||
title: Code. Simply. Clearly. Calmly.
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/calmcode.jpg
|
||||
|
||||
@@ -4,3 +4,6 @@ logins:
|
||||
- investsuite
|
||||
- vimsoHQ
|
||||
- mikeckennedy
|
||||
- koaning
|
||||
- deepset-ai
|
||||
- cryptapi
|
||||
|
||||
@@ -6,21 +6,9 @@ Being able to use asynchronous functions in your tests could be useful, for exam
|
||||
|
||||
Let's look at how we can make that work.
|
||||
|
||||
## pytest-asyncio
|
||||
## pytest.mark.anyio
|
||||
|
||||
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. Pytest provides a neat library for this, called `pytest-asyncio`, that allows us to specify that some test functions are to be called asynchronously.
|
||||
|
||||
You can install it via:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pytest-asyncio
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. Anyio provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.
|
||||
|
||||
## HTTPX
|
||||
|
||||
@@ -66,7 +54,7 @@ $ pytest
|
||||
|
||||
## In Detail
|
||||
|
||||
The marker `@pytest.mark.asyncio` tells pytest that this test function should be called asynchronously:
|
||||
The marker `@pytest.mark.anyio` tells pytest that this test function should be called asynchronously:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
@@ -97,4 +85,4 @@ that we used to make our requests with the `TestClient`.
|
||||
As the testing function is now asynchronous, you can now also call (and `await`) other `async` functions apart from sending requests to your FastAPI application in your tests, exactly as you would call them anywhere else in your code.
|
||||
|
||||
!!! tip
|
||||
If you encounter a `RuntimeError: Task attached to a different loop` when integrating asynchronous function calls in your tests (e.g. when using <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>) check out <a href="https://github.com/pytest-dev/pytest-asyncio/issues/38#issuecomment-264418154" class="external-link" target="_blank">this issue</a> in the pytest-asyncio repository.
|
||||
If you encounter a `RuntimeError: Task attached to a different loop` when integrating asynchronous function calls in your tests (e.g. when using <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>) Remember to instantiate objects that need an event loop only within async functions, e.g. an `'@app.on_event("startup")` callback.
|
||||
|
||||
@@ -152,21 +152,6 @@ After that, your file structure could look like:
|
||||
└── swagger-ui.css
|
||||
```
|
||||
|
||||
### Install `aiofiles`
|
||||
|
||||
Now you need to install `aiofiles`:
|
||||
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install aiofiles
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Serve the static files
|
||||
|
||||
* Import `StaticFiles`.
|
||||
@@ -248,3 +233,82 @@ Now, to be able to test that everything works, create a *path operation*:
|
||||
Now, you should be able to disconnect your WiFi, go to your docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, and reload the page.
|
||||
|
||||
And even without Internet, you would be able to see the docs for your API and interact with it.
|
||||
|
||||
## Configuring Swagger UI
|
||||
|
||||
You can configure some extra <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">Swagger UI parameters</a>.
|
||||
|
||||
To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function.
|
||||
|
||||
`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly.
|
||||
|
||||
FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs.
|
||||
|
||||
### Disable Syntax Highlighting
|
||||
|
||||
For example, you could disable syntax highlighting in Swagger UI.
|
||||
|
||||
Without changing the settings, syntax highlighting is enabled by default:
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image02.png">
|
||||
|
||||
But you can disable it by setting `syntaxHighlight` to `False`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/extending_openapi/tutorial003.py!}
|
||||
```
|
||||
|
||||
...and then Swagger UI won't show the syntax highlighting anymore:
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image03.png">
|
||||
|
||||
### Change the Theme
|
||||
|
||||
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle):
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/extending_openapi/tutorial004.py!}
|
||||
```
|
||||
|
||||
That configuration would change the syntax highlighting color theme:
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image04.png">
|
||||
|
||||
### Change Default Swagger UI Parameters
|
||||
|
||||
FastAPI includes some default configuration parameters appropriate for most of the use cases.
|
||||
|
||||
It includes these default configurations:
|
||||
|
||||
```Python
|
||||
{!../../../fastapi/openapi/docs.py[ln:7-13]!}
|
||||
```
|
||||
|
||||
You can override any of them by setting a different value in the argument `swagger_ui_parameters`.
|
||||
|
||||
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/extending_openapi/tutorial005.py!}
|
||||
```
|
||||
|
||||
### Other Swagger UI Parameters
|
||||
|
||||
To see all the other possible configurations you can use, read the official <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">docs for Swagger UI parameters</a>.
|
||||
|
||||
### JavaScript-only settings
|
||||
|
||||
Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions).
|
||||
|
||||
FastAPI also includes these JavaScript-only `presets` settings:
|
||||
|
||||
```JavaScript
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||||
]
|
||||
```
|
||||
|
||||
These are **JavaScript** objects, not strings, so you can't pass them from Python code directly.
|
||||
|
||||
If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need.
|
||||
|
||||
@@ -1,44 +1,56 @@
|
||||
# GraphQL
|
||||
|
||||
**FastAPI** has optional support for GraphQL (provided by Starlette directly), using the `graphene` library.
|
||||
As **FastAPI** is based on the **ASGI** standard, it's very easy to integrate any **GraphQL** library also compatible with ASGI.
|
||||
|
||||
You can combine normal FastAPI *path operations* with GraphQL on the same application.
|
||||
|
||||
## Import and use `graphene`
|
||||
!!! tip
|
||||
**GraphQL** solves some very specific use cases.
|
||||
|
||||
GraphQL is implemented with Graphene, you can check <a href="https://docs.graphene-python.org/en/latest/quickstart/" class="external-link" target="_blank">Graphene's docs</a> for more details.
|
||||
It has **advantages** and **disadvantages** when compared to common **web APIs**.
|
||||
|
||||
Import `graphene` and define your GraphQL data:
|
||||
Make sure you evaluate if the **benefits** for your use case compensate the **drawbacks**. 🤓
|
||||
|
||||
```Python hl_lines="1 6-10"
|
||||
## GraphQL Libraries
|
||||
|
||||
Here are some of the **GraphQL** libraries that have **ASGI** support. You could use them with **FastAPI**:
|
||||
|
||||
* <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a> 🍓
|
||||
* With <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">docs for FastAPI</a>
|
||||
* <a href="https://ariadnegraphql.org/" class="external-link" target="_blank">Ariadne</a>
|
||||
* With <a href="https://ariadnegraphql.org/docs/starlette-integration" class="external-link" target="_blank">docs for Starlette</a> (that also apply to FastAPI)
|
||||
* <a href="https://tartiflette.io/" class="external-link" target="_blank">Tartiflette</a>
|
||||
* With <a href="https://tartiflette.github.io/tartiflette-asgi/" class="external-link" target="_blank">Tartiflette ASGI</a> to provide ASGI integration
|
||||
* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
|
||||
* With <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>
|
||||
|
||||
## GraphQL with Strawberry
|
||||
|
||||
If you need or want to work with **GraphQL**, <a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a> is the **recommended** library as it has the design closest to **FastAPI's** design, it's all based on **type annotations**.
|
||||
|
||||
Depending on your use case, you might prefer to use a different library, but if you asked me, I would probably suggest you try **Strawberry**.
|
||||
|
||||
Here's a small preview of how you could integrate Strawberry with FastAPI:
|
||||
|
||||
```Python hl_lines="3 22 25-26"
|
||||
{!../../../docs_src/graphql/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Add Starlette's `GraphQLApp`
|
||||
You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>.
|
||||
|
||||
Then import and add Starlette's `GraphQLApp`:
|
||||
And also the docs about <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">Strawberry with FastAPI</a>.
|
||||
|
||||
```Python hl_lines="3 14"
|
||||
{!../../../docs_src/graphql/tutorial001.py!}
|
||||
```
|
||||
## Older `GraphQLApp` from Starlette
|
||||
|
||||
!!! info
|
||||
Here we are using `.add_route`, that is the way to add a route in Starlette (inherited by FastAPI) without declaring the specific operation (as would be with `.get()`, `.post()`, etc).
|
||||
Previous versions of Starlette included a `GraphQLApp` class to integrate with <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>.
|
||||
|
||||
## Check it
|
||||
It was deprecated from Starlette, but if you have code that used it, you can easily **migrate** to <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>, that covers the same use case and has an **almost identical interface**.
|
||||
|
||||
Run it with Uvicorn and open your browser at <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
!!! tip
|
||||
If you need GraphQL, I still would recommend you check out <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a>, as it's based on type annotations instead of custom classes and types.
|
||||
|
||||
You will see GraphiQL web user interface:
|
||||
## Learn More
|
||||
|
||||
<img src="/img/tutorial/graphql/image01.png">
|
||||
You can learn more about **GraphQL** in the <a href="https://graphql.org/" class="external-link" target="_blank">official GraphQL documentation</a>.
|
||||
|
||||
## More details
|
||||
|
||||
For more details, including:
|
||||
|
||||
* Accessing request information
|
||||
* Adding background tasks
|
||||
* Using normal or async functions
|
||||
|
||||
check the official <a href="https://www.starlette.io/graphql/" class="external-link" target="_blank">Starlette GraphQL docs</a>.
|
||||
You can also read more about each those libraries described above in their links.
|
||||
|
||||
@@ -77,7 +77,7 @@ This *path operation*-specific OpenAPI schema is normally generated automaticall
|
||||
!!! tip
|
||||
This is a low level extension point.
|
||||
|
||||
If you only need to declare additonal responses, a more convenient way to do it is with [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}.
|
||||
If you only need to declare additional responses, a more convenient way to do it is with [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
You can extend the OpenAPI schema for a *path operation* using the parameter `openapi_extra`.
|
||||
|
||||
|
||||
@@ -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.
|
||||
A common choice is Jinja2, the same one used by Flask and other tools.
|
||||
|
||||
There are utilities to configure it easily that you can use directly in your **FastAPI** application (provided by Starlette).
|
||||
|
||||
@@ -20,18 +20,6 @@ $ pip install jinja2
|
||||
|
||||
</div>
|
||||
|
||||
If you need to also serve static files (as in this example), install `aiofiles`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install aiofiles
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Using `Jinja2Templates`
|
||||
|
||||
* Import `Jinja2Templates`.
|
||||
|
||||
@@ -242,8 +242,6 @@ It was one of the first extremely fast Python frameworks based on `asyncio`. It
|
||||
|
||||
Falcon is another high performance Python framework, it is designed to be minimal, and work as the foundation of other frameworks like Hug.
|
||||
|
||||
It uses the previous standard for Python web frameworks (WSGI) which is synchronous, so it can't handle WebSockets and other use cases. Nevertheless, it also has a very good performance.
|
||||
|
||||
It is designed to have functions that receive two parameters, one "request" and one "response". Then you "read" parts from the request, and "write" parts to the response. Because of this design, it is not possible to declare request parameters and bodies with standard Python type hints as function parameters.
|
||||
|
||||
So, data validation, serialization, and documentation, have to be done in code, not automatically. Or they have to be implemented as a framework on top of Falcon, like Hug. This same distinction happens in other frameworks that are inspired by Falcon's design, of having one request object and one response object as parameters.
|
||||
@@ -367,7 +365,6 @@ It has:
|
||||
|
||||
* Seriously impressive performance.
|
||||
* WebSocket support.
|
||||
* GraphQL support.
|
||||
* In-process background tasks.
|
||||
* Startup and shutdown events.
|
||||
* Test client built on requests.
|
||||
@@ -375,7 +372,7 @@ It has:
|
||||
* Session and Cookie support.
|
||||
* 100% test coverage.
|
||||
* 100% type annotated codebase.
|
||||
* Zero hard dependencies.
|
||||
* Few hard dependencies.
|
||||
|
||||
Starlette is currently the fastest Python framework tested. Only surpassed by Uvicorn, which is not a framework, but a server.
|
||||
|
||||
|
||||
@@ -324,7 +324,15 @@ So, about the egg and the chicken, how do you call the first `async` function?
|
||||
|
||||
If you are working with **FastAPI** you don't have to worry about that, because that "first" function will be your *path operation function*, and FastAPI will know how to do the right thing.
|
||||
|
||||
But if you want to use `async` / `await` without FastAPI, <a href="https://docs.python.org/3/library/asyncio-task.html#coroutine" class="external-link" target="_blank">check the official Python docs</a>.
|
||||
But if you want to use `async` / `await` without FastAPI, you can do it as well.
|
||||
|
||||
### Write your own async code
|
||||
|
||||
Starlette (and **FastAPI**) are based on <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, which makes it compatible with both Python's standard library <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> and <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
|
||||
In particular, you can directly use <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> for your advanced concurrency use cases that require more advanced patterns in your own code.
|
||||
|
||||
And even if you were not using FastAPI, you could also write your own async applications with <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> to be highly compatible and get its benefits (e.g. *structured concurrency*).
|
||||
|
||||
### Other forms of asynchronous code
|
||||
|
||||
|
||||
@@ -163,20 +163,6 @@ 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 using `--symlink` (or `--pth-file` on Windows).
|
||||
|
||||
### 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.
|
||||
@@ -340,7 +326,7 @@ docs/es/docs/features.md
|
||||
* Now open the MkDocs config file for English at:
|
||||
|
||||
```
|
||||
docs/en/docs/mkdocs.yml
|
||||
docs/en/mkdocs.yml
|
||||
```
|
||||
|
||||
* Find the place where that `docs/features.md` is located in the config file. Somewhere like:
|
||||
@@ -359,7 +345,7 @@ nav:
|
||||
* Open the MkDocs config file for the language you are editing, e.g.:
|
||||
|
||||
```
|
||||
docs/es/docs/mkdocs.yml
|
||||
docs/es/mkdocs.yml
|
||||
```
|
||||
|
||||
* Add it there at the exact same location it was for English, e.g.:
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
.termynal-comment {
|
||||
color: #4a968f;
|
||||
font-style: italic;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.termy [data-termynal] {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
a.external-link::after {
|
||||
/* \00A0 is a non-breaking space
|
||||
to make the mark be on the same line as the link
|
||||
@@ -12,6 +22,10 @@ a.internal-link::after {
|
||||
content: "\00A0↪";
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: 5px 5px 10px #999;
|
||||
}
|
||||
|
||||
/* Give space to lower icons so Gitter chat doesn't get on top of them */
|
||||
.md-footer-meta {
|
||||
padding-bottom: 2em;
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
max-width: 100%;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 18px;
|
||||
/* font-size: 18px; */
|
||||
font-size: 15px;
|
||||
/* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */
|
||||
font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;
|
||||
border-radius: 4px;
|
||||
|
||||
311
docs/en/docs/deployment/concepts.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Deployments Concepts
|
||||
|
||||
When deploying a **FastAPI** application, or actually, any type of web API, there are several concepts that you probably care about, and using them you can find the **most appropriate** way to **deploy your application**.
|
||||
|
||||
Some of the important concepts are:
|
||||
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
We'll see how they would affect **deployments**.
|
||||
|
||||
In the end, the ultimate objective is to be able to **serve your API clients** in a way that is **secure**, to **avoid disruptions**, and to use the **compute resources** (for example remote servers/virtual machines) as efficiently as possible. 🚀
|
||||
|
||||
I'll tell you a bit more about these **concepts** here, and that would hopefully give you the **intuition** you would need to decide how to deploy your API in very different environments, possibly even in **future** ones that don't exist yet.
|
||||
|
||||
By considering these concepts, you will be able to **evaluate and design** the best way to deploy **your own APIs**.
|
||||
|
||||
In the next chapters, I'll give you more **concrete recipes** to deploy FastAPI applications.
|
||||
|
||||
But for now, let's check these important **conceptual ideas**. These concepts also apply to any other type of web API. 💡
|
||||
|
||||
## Security - HTTPS
|
||||
|
||||
In the [previous chapter about HTTPS](./https.md){.internal-link target=_blank} we learned about how HTTPS provides encryption for your API.
|
||||
|
||||
We also saw that HTTPS is normally provided by a component **external** to your application server, a **TLS Termination Proxy**.
|
||||
|
||||
And there has to be something in charge of **renewing the HTTPS certificates**, it could be the same component or it could be something different.
|
||||
|
||||
### Example Tools for HTTPS
|
||||
|
||||
Some of the tools you could use as a TLS Termination Proxy are:
|
||||
|
||||
* Traefik
|
||||
* Automatically handles certificates renewals ✨
|
||||
* Caddy
|
||||
* Automatically handles certificates renewals ✨
|
||||
* Nginx
|
||||
* With an external component like Certbot for certificate renewals
|
||||
* HAProxy
|
||||
* With an external component like Certbot for certificate renewals
|
||||
* Kubernetes with an Ingress Controller like Nginx
|
||||
* With an external component like cert-manager for certificate renewals
|
||||
* Handled internally by a cloud provider as part of their services (read below 👇)
|
||||
|
||||
Another option is that you could use a **cloud service** that does more of the work including setting up HTTPS. It could have some restrictions or charge you more, etc. But in that case, you wouldn't have to set up a TLS Termination Proxy yourself.
|
||||
|
||||
I'll show you some concrete examples in the next chapters.
|
||||
|
||||
---
|
||||
|
||||
Then the next concepts to consider are all about the program running your actual API (e.g. Uvicorn).
|
||||
|
||||
## Program and Process
|
||||
|
||||
We will talk a lot about the running "**process**", so it's useful to have clarity about what it means, and what's the difference with the word "**program**".
|
||||
|
||||
### What is a Program
|
||||
|
||||
The word **program** is commonly used to describe many things:
|
||||
|
||||
* The **code** that you write, the **Python files**.
|
||||
* The **file** that can be **executed** by the operating system, for example: `python`, `python.exe` or `uvicorn`.
|
||||
* A particular program while it is **running** on the operating system, using the CPU, and storing things on memory. This is also called a **process**.
|
||||
|
||||
### What is a Process
|
||||
|
||||
The word **process** is normally used in a more specific way, only referring to the thing that is running in the operating system (like in the last point above):
|
||||
|
||||
* A particular program while it is **running** on the operating system.
|
||||
* This doesn't refer to the file, nor to the code, it refers **specifically** to the thing that is being **executed** and managed by the operating system.
|
||||
* Any program, any code, **can only do things** when it is being **executed**. So, when there's a **process running**.
|
||||
* The process can be **terminated** (or "killed") by you, or by the operating system. At that point, it stops running/being executed, and it can **no longer do things**.
|
||||
* Each application that you have running on your computer has some process behind it, each running program, each window, etc. And there are normally many processes running **at the same time** while a computer is on.
|
||||
* There can be **multiple processes** of the **same program** running at the same time.
|
||||
|
||||
If you check out the "task manager" or "system monitor" (or similar tools) in your operating system, you will be able to see many of those processes running.
|
||||
|
||||
And, for example, you will probably see that there are multiple processes running the same browser program (Firefox, Chrome, Edge, etc). They normally run one process per tab, plus some other extra processes.
|
||||
|
||||
<img class="shadow" src="/img/deployment/concepts/image01.png">
|
||||
|
||||
---
|
||||
|
||||
Now that we know the difference between the terms **process** and **program**, let's continue talking about deployments.
|
||||
|
||||
## Running on Startup
|
||||
|
||||
In most cases, when you create a web API, you want it to be **always running**, uninterrupted, so that your clients can always access it. This is of course, unless you have a specific reason why you want it to run only in certain situations, but most of the time you want it constantly running and **available**.
|
||||
|
||||
### In a Remote Server
|
||||
|
||||
When you set up a remote server (a cloud server, a virtual machine, etc.) the simplest thing you can do is to run Uvicorn (or similar) manually, the same way you do when developing locally.
|
||||
|
||||
And it will work and will be useful **during development**.
|
||||
|
||||
But if your connection to the server is lost, the **running process** will probably die.
|
||||
|
||||
And if the server is restarted (for example after updates, or migrations from the cloud provider) you probably **won't notice it**. And because of that, you won't even know that you have to restart the process manually. So, your API will just stay dead. 😱
|
||||
|
||||
### Run Automatically on Startup
|
||||
|
||||
In general, you will probably want the server program (e.g. Uvicorn) to be started automatically on server startup, and without needing any **human intervention**, to have a process always running with your API (e.g. Uvicorn running your FastAPI app).
|
||||
|
||||
### Separate Program
|
||||
|
||||
To achieve this, you will normally have a **separate program** that would make sure your application is run on startup. And in many cases, it would also make sure other components or applications are also run, for example, a database.
|
||||
|
||||
### Example Tools to Run at Startup
|
||||
|
||||
Some examples of the tools that can do this job are:
|
||||
|
||||
* Docker
|
||||
* Kubernetes
|
||||
* Docker Compose
|
||||
* Docker in Swarm Mode
|
||||
* Systemd
|
||||
* Supervisor
|
||||
* Handled internally by a cloud provider as part of their services
|
||||
* Others...
|
||||
|
||||
I'll give you more concrete examples in the next chapters.
|
||||
|
||||
## Restarts
|
||||
|
||||
Similar to making sure your application is run on startup, you probably also want to make sure it is **restarted** after failures.
|
||||
|
||||
### We Make Mistakes
|
||||
|
||||
We, as humans, make **mistakes**, all the time. Software almost *always* has **bugs** hidden in different places. 🐛
|
||||
|
||||
And we as developers keep improving the code as we find those bugs and as we implement new features (possibly adding new bugs too 😅).
|
||||
|
||||
### Small Errors Automatically Handled
|
||||
|
||||
When building web APIs with FastAPI, if there's an error in our code, FastAPI will normally contain it to the single request that triggered the error. 🛡
|
||||
|
||||
The client will get a **500 Internal Server Error** for that request, but the application will continue working for the next requests instead of just crashing completely.
|
||||
|
||||
### Bigger Errors - Crashes
|
||||
|
||||
Nevertheless, there might be cases where we write some code that **crashes the entire application** making Uvicorn and Python crash. 💥
|
||||
|
||||
And still, you would probably not want the application to stay dead because there was an error in one place, you probably want it to **continue running** at least for the *path operations* that are not broken.
|
||||
|
||||
### Restart After Crash
|
||||
|
||||
But in those cases with really bad errors that crash the running **process**, you would want an external component that is in charge of **restarting** the process, at least a couple of times...
|
||||
|
||||
!!! tip
|
||||
...Although if the whole application is just **crashing immediately** it probably doesn't make sense to keep restarting it forever. But in those cases, you will probably notice it during development, or at least right after deployment.
|
||||
|
||||
So let's focus on the main cases, where it could crash entirely in some particular cases **in the future**, and it still makes sense to restart it.
|
||||
|
||||
You would probably want to have the thing in charge of restarting your application as an **external component**, because by that point, the same application with Uvicorn and Python already crashed, so there's nothing in the same code of the same app that could do anything about it.
|
||||
|
||||
### Example Tools to Restart Automatically
|
||||
|
||||
In most cases, the same tool that is used to **run the program on startup** is also used to handle automatic **restarts**.
|
||||
|
||||
For example, this could be handled by:
|
||||
|
||||
* Docker
|
||||
* Kubernetes
|
||||
* Docker Compose
|
||||
* Docker in Swarm Mode
|
||||
* Systemd
|
||||
* Supervisor
|
||||
* Handled internally by a cloud provider as part of their services
|
||||
* Others...
|
||||
|
||||
## Replication - Processes and Memory
|
||||
|
||||
With a FastAPI application, using a server program like Uvicorn, running it once in **one process** can serve multiple clients concurrently.
|
||||
|
||||
But in many cases, you will want to run several worker processes at the same time.
|
||||
|
||||
### Multiple Processes - Workers
|
||||
|
||||
If you have more clients than what a single process can handle (for example if the virtual machine is not too big) and you have **multiple cores** in the server's CPU, then you could have **multiple processes** running with the same application at the same time, and distribute all the requests among them.
|
||||
|
||||
When you run **multiple processes** of the same API program, they are commonly called **workers**.
|
||||
|
||||
### Worker Processes and Ports
|
||||
|
||||
Remember from the docs [About HTTPS](./https.md){.internal-link target=_blank} that only one process can be listening on one combination of port and IP address in a server?
|
||||
|
||||
This is still true.
|
||||
|
||||
So, to be able to have **multiple processes** at the same time, there has to be a **single process listening on a port** that then transmits the communication to each worker process in some way.
|
||||
|
||||
### Memory per Process
|
||||
|
||||
Now, when the program loads things in memory, for example, a machine learning model in a variable, or the contents of a large file in a variable, all that **consumes a bit of the memory (RAM)** of the server.
|
||||
|
||||
And multiple processes normally **don't share any memory**. This means that each running process has its own things, variables, and memory. And if you are consuming a large amount of memory in your code, **each process** will consume an equivalent amount of memory.
|
||||
|
||||
### Server Memory
|
||||
|
||||
For example, if your code loads a Machine Learning model with **1 GB in size**, when you run one process with your API, it will consume at least 1 GB of RAM. And if you start **4 processes** (4 workers), each will consume 1 GB of RAM. So in total, your API will consume **4 GB of RAM**.
|
||||
|
||||
And if your remote server or virtual machine only has 3 GB of RAM, trying to load more than 4 GB of RAM will cause problems. 🚨
|
||||
|
||||
### Multiple Processes - An Example
|
||||
|
||||
In this example, there's a **Manager Process** that starts and controls two **Worker Processes**.
|
||||
|
||||
This Manager Process would probably be the one listening on the **port** in the IP. And it would transmit all the communication to the worker processes.
|
||||
|
||||
Those worker processes would be the ones running your application, they would perform the main computations to receive a **request** and return a **response**, and they would load anything you put in variables in RAM.
|
||||
|
||||
<img src="/img/deployment/concepts/process-ram.svg">
|
||||
|
||||
And of course, the same machine would probably have **other processes** running as well, apart from your application.
|
||||
|
||||
An interesting detail is that the percentage of the **CPU used** by each process can **vary** a lot over time, but the **memory (RAM)** normally stays more or less **stable**.
|
||||
|
||||
If you have an API that does a comparable amount of computations every time and you have a lot of clients, then the **CPU utilization** will probably *also be stable* (instead of constantly going up and down quickly).
|
||||
|
||||
### Examples of Replication Tools and Strategies
|
||||
|
||||
There can be several approaches to achieve this, and I'll tell you more about specific strategies in the next chapters, for example when talking about Docker and containers.
|
||||
|
||||
The main constraint to consider is that there has to be a **single** component handling the **port** in the **public IP**. And then it has to have a way to **transmit** the communication to the replicated **processes/workers**.
|
||||
|
||||
Here are some possible combinations and strategies:
|
||||
|
||||
* **Gunicorn** managing **Uvicorn workers**
|
||||
* Gunicorn would be the **process manager** listening on the **IP** and **port**, the replication would be by having **multiple Uvicorn worker processes**
|
||||
* **Uvicorn** managing **Uvicorn workers**
|
||||
* One Uvicorn **process manager** would listen on the **IP** and **port**, and it would start **multiple Uvicorn worker processes**
|
||||
* **Kubernetes** and other distributed **container systems**
|
||||
* Something in the **Kubernetes** layer would listen on the **IP** and **port**. The replication would be by having **multiple containers**, each with **one Uvicorn process** running
|
||||
* **Cloud services** that handle this for your
|
||||
* The cloud service will probably **handle replication for you**. It would possibly let you define **a process to run**, or a **container image** to use, in any case, it would most probably be **a single Uvicorn process**, and the cloud service would be in charge of replicating it.
|
||||
|
||||
!!! tip
|
||||
Don't worry if some of these items about **containers**, Docker, or Kubernetes don't make a lot of sense yet.
|
||||
|
||||
I'll tell you more about container images, Docker, Kubernetes, etc. in a future chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}.
|
||||
|
||||
## Previous Steps Before Starting
|
||||
|
||||
There are many cases where you want to perform some steps **before starting** your application.
|
||||
|
||||
For example, you might want to run **database migrations**.
|
||||
|
||||
But in most cases, you will want to perform these steps only **once**.
|
||||
|
||||
So, you will want to have a **single process** to perform those **previous steps**, before starting the application.
|
||||
|
||||
And you will have to make sure that it's a single process running those previous steps *even* if afterwards, you start **multiple processes** (multiple workers) for the application itself. If those steps were run by **multiple processes**, they would **duplicate** the work by running it on **parallel**, and if the steps were something delicate like a database migration, they could cause conflicts with each other.
|
||||
|
||||
Of course, there are some cases where there's no problem in running the previous steps multiple times, in that case, it's a lot easier to handle.
|
||||
|
||||
!!! tip
|
||||
Also, have in mind that depending on your setup, in some cases you **might not even need any previous steps** before starting your application.
|
||||
|
||||
In that case, you wouldn't have to worry about any of this. 🤷
|
||||
|
||||
### Examples of Previous Steps Strategies
|
||||
|
||||
This will **depend heavily** on the way you **deploy your system**, and it would probably be connected to the way you start programs, handling restarts, etc.
|
||||
|
||||
Here are some possible ideas:
|
||||
|
||||
* An "Init Container" in Kubernetes that runs before your app container
|
||||
* A bash script that runs the previous steps and then starts your application
|
||||
* You would still need a way to start/restart *that* bash script, detect errors, etc.
|
||||
|
||||
!!! tip
|
||||
I'll give you more concrete examples for doing this with containers in a future chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}.
|
||||
|
||||
## Resource Utilization
|
||||
|
||||
Your server(s) is (are) a **resource**, you can consume or **utilize**, with your programs, the computation time on the CPUs, and the RAM memory available.
|
||||
|
||||
How much of the system resources do you want to be consuming/utilizing? It might be easy to think "not much", but in reality, you will probably want to consume **as much as possible without crashing**.
|
||||
|
||||
If you are paying for 3 servers but you are using only a little bit of their RAM and CPU, you are probably **wasting money** 💸, and probably **wasting server electric power** 🌎, etc.
|
||||
|
||||
In that case, it could be better to have only 2 servers and use a higher percentage of their resources (CPU, memory, disk, network bandwidth, etc).
|
||||
|
||||
On the other hand, if you have 2 servers and you are using **100% of their CPU and RAM**, at some point one process will ask for more memory, and the server will have to use the disk as "memory" (which can be thousands of times slower), or even **crash**. Or one process might need to do some computation and would have to wait until the CPU is free again.
|
||||
|
||||
In this case, it would be better to get **one extra server** and run some processes on it so that they all have **enough RAM and CPU time**.
|
||||
|
||||
There's also the chance that for some reason you have a **spike** of usage of your API. Maybe it went viral, or maybe some other services or bots start using it. And you might want to have extra resources to be safe in those cases.
|
||||
|
||||
You could put an **arbitrary number** to target, for example, something **between 50% to 90%** of resource utilization. The point is that those are probably the main things you will want to measure and use to tweak your deployments.
|
||||
|
||||
You can use simple tools like `htop` to see the CPU and RAM used in your server or the amount used by each process. Or you can use more complex monitoring tools, which may be distributed across servers, etc.
|
||||
|
||||
## Recap
|
||||
|
||||
You have been reading here some of the main concepts that you would probably need to have in mind when deciding how to deploy your application:
|
||||
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
Understanding these ideas and how to apply them should give you the intuition necessary to take any decisions when configuring and tweaking your deployments. 🤓
|
||||
|
||||
In the next sections, I'll give you more concrete examples of possible strategies you can follow. 🚀
|
||||
@@ -9,7 +9,7 @@ It will take you about **10 minutes**.
|
||||
|
||||
## A basic **FastAPI** app
|
||||
|
||||
* Create a directory for your app, for example `./fastapideta/` and enter in it.
|
||||
* Create a directory for your app, for example, `./fastapideta/` and enter into it.
|
||||
|
||||
### FastAPI code
|
||||
|
||||
@@ -213,7 +213,7 @@ Now you can share that URL with anyone and they will be able to access your API.
|
||||
|
||||
Congrats! You deployed your FastAPI app to Deta! 🎉 🍰
|
||||
|
||||
Also notice that Deta correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your clients will have a secure encrypted connection. ✅ 🔒
|
||||
Also, notice that Deta correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your clients will have a secure encrypted connection. ✅ 🔒
|
||||
|
||||
## Check the Visor
|
||||
|
||||
@@ -235,6 +235,24 @@ You can also edit them and re-play them.
|
||||
|
||||
## Learn more
|
||||
|
||||
At some point you will probably want to store some data for your app in a way that persists through time. For that you can use <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a>, it also has a generous **free tier**.
|
||||
At some point, you will probably want to store some data for your app in a way that persists through time. For that you can use <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a>, it also has a generous **free tier**.
|
||||
|
||||
You can also read more in the <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">Deta Docs</a>.
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
Coming back to the concepts we discussed in [Deployments Concepts](./concepts.md){.internal-link target=_blank}, here's how each of them would be handled with Deta:
|
||||
|
||||
* **HTTPS**: Handled by Deta, they will give you a subdomain and handle HTTPS automatically.
|
||||
* **Running on startup**: Handled by Deta, as part of their service.
|
||||
* **Restarts**: Handled by Deta, as part of their service.
|
||||
* **Replication**: Handled by Deta, as part of their service.
|
||||
* **Memory**: Limit predefined by Deta, you could contact them to increase it.
|
||||
* **Previous steps before starting**: Not directly supported, you could make it work with their Cron system or additional scripts.
|
||||
|
||||
!!! note
|
||||
Deta is designed to make it easy (and free) to deploy simple applications quickly.
|
||||
|
||||
It can simplify several use cases, but at the same time, it doesn't support others, like using external databases (apart from Deta's own NoSQL database system), custom virtual machines, etc.
|
||||
|
||||
You can read more details in the <a href="https://docs.deta.sh/docs/micros/about/" class="external-link" target="_blank">Deta docs</a> to see if it's the right choice for you.
|
||||
|
||||
@@ -1,67 +1,144 @@
|
||||
# Deploy with Docker
|
||||
# FastAPI in Containers - Docker
|
||||
|
||||
In this section you'll see instructions and links to guides to know how to:
|
||||
When deploying FastAPI applications a common approach is to build a **Linux container image**. It's normally done using <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. You can then deploy that container image in one of a few possible ways.
|
||||
|
||||
* Make your **FastAPI** application a Docker image/container with maximum performance. In about **5 min**.
|
||||
* (Optionally) understand what you, as a developer, need to know about HTTPS.
|
||||
* 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 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:
|
||||
|
||||
## <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
|
||||
|
||||
This image has an "auto-tuning" mechanism included, so that you can just add your code and get very high performance automatically. And without making sacrifices.
|
||||
|
||||
But you can still change and update all the configurations with environment variables or configuration files.
|
||||
Using Linux containers has several advantages including **security**, **replicability**, **simplicity**, and others.
|
||||
|
||||
!!! tip
|
||||
To see all the configurations and options, go to the Docker image page: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
In a hurry and already know this stuff? Jump to the [`Dockerfile` below 👇](#build-a-docker-image-for-fastapi).
|
||||
|
||||
## Create a `Dockerfile`
|
||||
|
||||
* Go to your project directory.
|
||||
* Create a `Dockerfile` with:
|
||||
<details>
|
||||
<summary>Dockerfile Preview 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
FROM python:3.9
|
||||
|
||||
COPY ./app /app
|
||||
```
|
||||
WORKDIR /code
|
||||
|
||||
### Bigger Applications
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
If you followed the section about creating [Bigger Applications with Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}, your `Dockerfile` might instead look like:
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
|
||||
COPY ./app /app/app
|
||||
```
|
||||
|
||||
### Raspberry Pi and other architectures
|
||||
|
||||
If you are running Docker in a Raspberry Pi (that has an ARM processor) or any other architecture, you can create a `Dockerfile` from scratch, based on a Python base image (that is multi-architecture) and use Uvicorn alone.
|
||||
|
||||
In this case, your `Dockerfile` could look like:
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.7
|
||||
|
||||
RUN pip install fastapi uvicorn
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
COPY ./app /app
|
||||
COPY ./app /code/app
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
|
||||
# If running behind a proxy like Nginx or Traefik add --proxy-headers
|
||||
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
|
||||
```
|
||||
|
||||
## Create the **FastAPI** Code
|
||||
</details>
|
||||
|
||||
* Create an `app` directory and enter in it.
|
||||
## What is a Container
|
||||
|
||||
Containers (mainly Linux containers) are a very **lightweight** way to package applications including all their dependencies and necessary files while keeping them isolated from other containers (other applications or components) in the same system.
|
||||
|
||||
Linux containers run using the same Linux kernel of the host (machine, virtual machine, cloud server, etc). This just means that they are very lightweight (compared to full virtual machines emulating an entire operating system).
|
||||
|
||||
This way, containers consume **little resources**, an amount comparable to running the processes directly (a virtual machine would consume much more).
|
||||
|
||||
Containers also have their own **isolated** running processes (commonly just one process), file system, and network, simplifying deployment, security, development, etc.
|
||||
|
||||
## What is a Container Image
|
||||
|
||||
A **container** is run from a **container image**.
|
||||
|
||||
A container image is a **static** version of all the files, environment variables, and the default command/program that should be present in a container. **Static** here means that the container **image** is not running, it's not being executed, it's only the packaged files and metadata.
|
||||
|
||||
In contrast to a "**container image**" that is the stored static contents, a "**container**" normally refers to the running instance, the thing that is being **executed**.
|
||||
|
||||
When the **container** is started and running (started from a **container image**) it could create or change files, environment variables, etc. Those changes will exist only in that container, but would not persist in the underlying container image (would not be saved to disk).
|
||||
|
||||
A container image is comparable to the **program** file and contents, e.g. `python` and some file `main.py`.
|
||||
|
||||
And the **container** itself (in contrast to the **container image**) is the actual running instance of the image, comparable to a **process**. In fact, a container is running only when it has a **process running** (and normally it's only a single process). The container stops when there's no process running in it.
|
||||
|
||||
## Container Images
|
||||
|
||||
Docker has been one of the main tools to create and manage **container images** and **containers**.
|
||||
|
||||
And there's a public <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> with pre-made **official container images** for many tools, environments, databases, and applications.
|
||||
|
||||
For example, there's an official <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python Image</a>.
|
||||
|
||||
And there are many other images for different things like databases, for example for:
|
||||
|
||||
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
|
||||
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
|
||||
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
|
||||
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>, etc.
|
||||
|
||||
By using a pre-made container image it's very easy to **combine** and use different tools. For example, to try out a new database. In most cases, you can use the **official images**, and just configure them with environment variables.
|
||||
|
||||
That way, in many cases you can learn about containers and Docker and re-use that knowledge with many different tools and components.
|
||||
|
||||
So, you would run **multiple containers** with different things, like a database, a Python application, a web server with a React frontend application, and connect them together via their internal network.
|
||||
|
||||
All the container management systems (like Docker or Kubernetes) have these networking features integrated into them.
|
||||
|
||||
## Containers and Processes
|
||||
|
||||
A **container image** normally includes in its metadata the default program or command that should be run when the **container** is started and the parameters to be passed to that program. Very similar to what would be if it was in the command line.
|
||||
|
||||
When a **container** is started, it will run that command/program (although you can override it and make it run a different command/program).
|
||||
|
||||
A container is running as long as the **main process** (command or program) is running.
|
||||
|
||||
A container normally has a **single process**, but it's also possible to start subprocesses from the main process, and that way you will have **multiple processes** in the same container.
|
||||
|
||||
But it's not possible to have a running container without **at least one running process**. If the main process stops, the container stops.
|
||||
|
||||
## Build a Docker Image for FastAPI
|
||||
|
||||
Okay, let's build something now! 🚀
|
||||
|
||||
I'll show you how to build a **Docker image** for FastAPI **from scratch**, based on the **official Python** image.
|
||||
|
||||
This is what you would want to do in **most cases**, for example:
|
||||
|
||||
* Using **Kubernetes** or similar tools
|
||||
* When running on a **Raspberry Pi**
|
||||
* Using a cloud service that would run a container image for you, etc.
|
||||
|
||||
### Package Requirements
|
||||
|
||||
You would normally have the **package requirements** for your application in some file.
|
||||
|
||||
It would depend mainly on the tool you use to **install** those requirements.
|
||||
|
||||
The most common way to do it is to have a file `requirements.txt` with the package names and their versions, one per line.
|
||||
|
||||
You would of course use the same ideas you read in [About FastAPI versions](./versions.md){.internal-link target=_blank} to set the ranges of versions.
|
||||
|
||||
For example, your `requirements.txt` could look like:
|
||||
|
||||
```
|
||||
fastapi>=0.68.0,<0.69.0
|
||||
pydantic>=1.8.0,<2.0.0
|
||||
uvicorn>=0.15.0,<0.16.0
|
||||
```
|
||||
|
||||
And you would normally install those package dependencies with `pip`, for example:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install -r requirements.txt
|
||||
---> 100%
|
||||
Successfully installed fastapi pydantic uvicorn
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
!!! info
|
||||
There are other formats and tools to define and install package dependencies.
|
||||
|
||||
I'll show you an example using Poetry later in a section below. 👇
|
||||
|
||||
### Create the **FastAPI** Code
|
||||
|
||||
* Create an `app` directory and enter it.
|
||||
* Create an empty file `__init__.py`.
|
||||
* Create a `main.py` file with:
|
||||
|
||||
```Python
|
||||
@@ -82,16 +159,126 @@ def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
* You should now have a directory structure like:
|
||||
### Dockerfile
|
||||
|
||||
Now in the same project directory create a file `Dockerfile` with:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
FROM python:3.9
|
||||
|
||||
# (2)
|
||||
WORKDIR /code
|
||||
|
||||
# (3)
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
# (4)
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (5)
|
||||
COPY ./app /code/app
|
||||
|
||||
# (6)
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
1. Start from the official Python base image.
|
||||
|
||||
2. Set the current working directory to `/code`.
|
||||
|
||||
This is where we'll put the `requirements.txt` file and the `app` directory.
|
||||
|
||||
3. Copy the file with the requirements to the `/code` directory.
|
||||
|
||||
Copy **only** the file with the requirements first, not the rest of the code.
|
||||
|
||||
As this file **doesn't change often**, Docker will detect it and use the **cache** for this step, enabling the cache for the next step too.
|
||||
|
||||
4. Install the package dependencies in the requirements file.
|
||||
|
||||
The `--no-cache-dir` option tells `pip` to not save the downloaded packages locally, as that is only if `pip` was going to be run again to install the same packages, but that's not the case when working with containers.
|
||||
|
||||
!!! note
|
||||
The `--no-cache-dir` is only related to `pip`, it has nothing to do with Docker or containers.
|
||||
|
||||
The `--upgrade` option tells `pip` to upgrade the packages if they are already installed.
|
||||
|
||||
Because the previous step copying the file could be detected by the **Docker cache**, this step will also **use the Docker cache** when available.
|
||||
|
||||
Using the cache in this step will **save** you a lot of **time** when building the image again and again during development, instead of **downloading and installing** all the dependencies **every time**.
|
||||
|
||||
5. Copy the `./app` directory inside the `/code` directory.
|
||||
|
||||
As this has all the code which is what **changes most frequently** the Docker **cache** won't be used for this or any **following steps** easily.
|
||||
|
||||
So, it's important to put this **near the end** of the `Dockerfile`, to optimize the container image build times.
|
||||
|
||||
6. Set the **command** to run the `uvicorn` server.
|
||||
|
||||
`CMD` takes a list of strings, each of these strings is what you would type in the command line separated by spaces.
|
||||
|
||||
This command will be run from the **current working directory**, the same `/code` directory you set above with `WORKDIR /code`.
|
||||
|
||||
Because the program will be started at `/code` and inside of it is the directory `./app` with your code, **Uvicorn** will be able to see and **import** `app` from `app.main`.
|
||||
|
||||
!!! tip
|
||||
Review what each line does by clicking each number bubble in the code. 👆
|
||||
|
||||
You should now have a directory structure like:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
└── Dockerfile
|
||||
├── Dockerfile
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
## Build the Docker image
|
||||
#### Behind a TLS Termination Proxy
|
||||
|
||||
If you are running your container behind a TLS Termination Proxy (load balancer) like Nginx or Traefik, add the option `--proxy-headers`, this will tell Uvicorn to trust the headers sent by that proxy telling it that the application is running behind HTTPS, etc.
|
||||
|
||||
```Dockerfile
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
#### Docker Cache
|
||||
|
||||
There's an important trick in this `Dockerfile`, we first copy the **file with the dependencies alone**, not the rest of the code. Let me tell you why is that.
|
||||
|
||||
```Dockerfile
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
```
|
||||
|
||||
Docker and other tools **build** these container images **incrementally**, adding **one layer on top of the other**, starting from the top of the `Dockerfile` and adding any files created by each of the instructions of the `Dockerfile`.
|
||||
|
||||
Docker and similar tools also use an **internal cache** when building the image, if a file hasn't changed since the last time building the container image, then it will **re-use the same layer** created the last time, instead of copying the file again and creating a new layer from scratch.
|
||||
|
||||
Just avoiding the copy of files doesn't necessarily improve things too much, but because it used the cache for that step, it can **use the cache for the next step**. For example, it could use the cache for the instruction that installs dependencies with:
|
||||
|
||||
```Dockerfile
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
```
|
||||
|
||||
The file with the package requirements **won't change frequently**. So, by copying only that file, Docker will be able to **use the cache** for that step.
|
||||
|
||||
And then, Docker will be able to **use the cache for the next step** that downloads and install those dependencies. And here's where we **save a lot of time**. ✨ ...and avoid boredom waiting. 😪😆
|
||||
|
||||
Downloading and installing the package dependencies **could take minutes**, but using the **cache** would **take seconds** at most.
|
||||
|
||||
And as you would be building the container image again and again during development to check that your code changes are working, there's a lot of accumulated time this would save.
|
||||
|
||||
Then, near the end of the `Dockerfile`, we copy all the code. As this is what **changes most frequently**, we put it near the end, because almost always, anything after this step will not be able to use the cache.
|
||||
|
||||
```Dockerfile
|
||||
COPY ./app /code/app
|
||||
```
|
||||
|
||||
### Build the Docker Image
|
||||
|
||||
Now that all the files are in place, let's build the container image.
|
||||
|
||||
* Go to the project directory (in where your `Dockerfile` is, containing your `app` directory).
|
||||
* Build your FastAPI image:
|
||||
@@ -106,7 +293,12 @@ $ docker build -t myimage .
|
||||
|
||||
</div>
|
||||
|
||||
## Start the Docker container
|
||||
!!! tip
|
||||
Notice the `.` at the end, it's equivalent to `./`, it tells Docker the directory to use to build the container image.
|
||||
|
||||
In this case, it's the same current directory (`.`).
|
||||
|
||||
### Start the Docker Container
|
||||
|
||||
* Run a container based on your image:
|
||||
|
||||
@@ -118,8 +310,6 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
||||
|
||||
</div>
|
||||
|
||||
Now you have an optimized FastAPI server in a Docker container. Auto-tuned for your current server (and number of CPU cores).
|
||||
|
||||
## Check it
|
||||
|
||||
You should be able to check it in your Docker container's URL, for example: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> or <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (or equivalent, using your Docker host).
|
||||
@@ -146,34 +336,363 @@ You will see the alternative automatic documentation (provided by <a href="https
|
||||
|
||||

|
||||
|
||||
## Traefik
|
||||
## Build a Docker Image with a Single-File FastAPI
|
||||
|
||||
<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a> is a high performance reverse proxy / load balancer. It can do the "TLS Termination Proxy" job (apart from other features).
|
||||
If your FastAPI is a single file, for example, `main.py` without an `./app` directory, your file structure could look like this:
|
||||
|
||||
It has integration with Let's Encrypt. So, it can handle all the HTTPS parts, including certificate acquisition and renewal.
|
||||
```
|
||||
.
|
||||
├── Dockerfile
|
||||
├── main.py
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
It also has integrations with Docker. So, you can declare your domains in each application configurations and have it read those configurations, generate the HTTPS certificates and serve HTTPS to your application automatically, without requiring any change in its configuration.
|
||||
Then you would just have to change the corresponding paths to copy the file inside the `Dockerfile`:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (1)
|
||||
COPY ./main.py /code/
|
||||
|
||||
# (2)
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
1. Copy the `main.py` file to the `/code` directory directly (without any `./app` directory).
|
||||
|
||||
2. Run Uvicorn and tell it to import the `app` object from `main` (instead of importing from `app.main`).
|
||||
|
||||
Then adjust the Uvicorn command to use the new module `main` instead of `app.main` to import the FastAPI object `app`.
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
Let's talk again about some of the same [Deployment Concepts](./concepts.md){.internal-link target=_blank} in terms of containers.
|
||||
|
||||
Containers are mainly a tool to simplify the process of **building and deploying** an application, but they don't enforce a particular approach to handle these **deployment concepts**, and there are several possible strategies.
|
||||
|
||||
The **good news** is that with each different strategy there's a way to cover all of the deployment concepts. 🎉
|
||||
|
||||
Let's review these **deployment concepts** in terms of containers:
|
||||
|
||||
* HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
## HTTPS
|
||||
|
||||
If we focus just on the **container image** for a FastAPI application (and later the running **container**), HTTPS normally would be handled **externally** by another tool.
|
||||
|
||||
It could be another container, for example with <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, handling **HTTPS** and **automatic** acquisition of **certificates**.
|
||||
|
||||
!!! tip
|
||||
Traefik has integrations with Docker, Kubernetes, and others, so it's very easy to set up and configure HTTPS for your containers with it.
|
||||
|
||||
Alternatively, HTTPS could be handled by a cloud provider as one of their services (while still running the application in a container).
|
||||
|
||||
## Running on Startup and Restarts
|
||||
|
||||
There is normally another tool in charge of **starting and running** your container.
|
||||
|
||||
It could be **Docker** directly, **Docker Compose**, **Kubernetes**, a **cloud service**, etc.
|
||||
|
||||
In most (or all) cases, there's a simple option to enable running the container on startup and enabling restarts on failures. For example, in Docker, it's the command line option `--restart`.
|
||||
|
||||
Without using containers, making applications run on startup and with restarts can be cumbersome and difficult. But when **working with containers** in most cases that functionality is included by default. ✨
|
||||
|
||||
## Replication - Number of Processes
|
||||
|
||||
If you have a <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr> of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Gunicorn with workers) in each container.
|
||||
|
||||
One of those distributed container management systems like Kubernetes normally has some integrated way of handling **replication of containers** while still supporting **load balancing** for the incoming requests. All at the **cluster level**.
|
||||
|
||||
In those cases, you would probably want to build a **Docker image from scratch** as [explained above](#dockerfile), installing your dependencies, and running **a single Uvicorn process** instead of running something like Gunicorn with Uvicorn workers.
|
||||
|
||||
### Load Balancer
|
||||
|
||||
When using containers, you would normally have some component **listening on the main port**. It could possibly be another container that is also a **TLS Termination Proxy** to handle **HTTPS** or some similar tool.
|
||||
|
||||
As this component would take the **load** of requests and distribute that among the workers in a (hopefully) **balanced** way, it is also commonly called a **Load Balancer**.
|
||||
|
||||
!!! tip
|
||||
The same **TLS Termination Proxy** component used for HTTPS would probably also be a **Load Balancer**.
|
||||
|
||||
And when working with containers, the same system you use to start and manage them would already have internal tools to transmit the **network communication** (e.g. HTTP requests) from that **load balancer** (that could also be a **TLS Termination Proxy**) to the container(s) with your app.
|
||||
|
||||
### One Load Balancer - Multiple Worker Containers
|
||||
|
||||
When working with **Kubernetes** or similar distributed container management systems, using their internal networking mechanisms would allow the single **load balancer** that is listening on the main **port** to transmit communication (requests) to possibly **multiple containers** running your app.
|
||||
|
||||
Each of these containers running your app would normally have **just one process** (e.g. a Uvicorn process running your FastAPI application). They would all be **identical containers**, running the same thing, but each with its own process, memory, etc. That way you would take advantage of **parallelization** in **different cores** of the CPU, or even in **different machines**.
|
||||
|
||||
And the distributed container system with the **load balancer** would **distribute the requests** to each one of the containers with your app **in turns**. So, each request could be handled by one of the multiple **replicated containers** running your app.
|
||||
|
||||
And normally this **load balancer** would be able to handle requests that go to *other* apps in your cluster (e.g. to a different domain, or under a different URL path prefix), and would transmit that communication to the right containers for *that other* application running in your cluster.
|
||||
|
||||
### One Process per Container
|
||||
|
||||
In this type of scenario, you probably would want to have **a single (Uvicorn) process per container**, as you would already be handling replication at the cluster level.
|
||||
|
||||
So, in this case, you **would not** want to have a process manager like Gunicorn with Uvicorn workers, or Uvicorn using its own Uvicorn workers. You would want to have just a **single Uvicorn process** per container (but probably multiple containers).
|
||||
|
||||
Having another process manager inside the container (as would be with Gunicorn or Uvicorn managing Uvicorn workers) would only add **unnecessary complexity** that you are most probably already taking care of with your cluster system.
|
||||
|
||||
### Containers with Multiple Processes and Special Cases
|
||||
|
||||
Of course, there are **special cases** where you could want to have **a container** with a **Gunicorn process manager** starting several **Uvicorn worker processes** inside.
|
||||
|
||||
In those cases, you can use the **official Docker image** that includes **Gunicorn** as a process manager running multiple **Uvicorn worker processes**, and some default settings to adjust the number of workers based on the current CPU cores automatically. I'll tell you more about it below in [Official Docker Image with Gunicorn - Uvicorn](#official-docker-image-with-gunicorn-uvicorn).
|
||||
|
||||
Here are some examples of when that could make sense:
|
||||
|
||||
#### A Simple App
|
||||
|
||||
You could want a process manager in the container if your application is **simple enough** that you don't need (at least not yet) to fine-tune the number of processes too much, and you can just use an automated default (with the official Docker image), and you are running it on a **single server**, not a cluster.
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
You could be deploying to a **single server** (not a cluster) with **Docker Compose**, so you wouldn't have an easy way to manage replication of containers (with Docker Compose) while preserving the shared network and **load balancing**.
|
||||
|
||||
Then you could want to have **a single container** with a **process manager** starting **several worker processes** inside.
|
||||
|
||||
#### Prometheus and Other Reasons
|
||||
|
||||
You could also have **other reasons** that would make it easier to have a **single container** with **multiple processes** instead of having **multiple containers** with **a single process** in each of them.
|
||||
|
||||
For example (depending on your setup) you could have some tool like a Prometheus exporter in the same container that should have access to **each of the requests** that come.
|
||||
|
||||
In this case, if you had **multiple containers**, by default, when Prometheus came to **read the metrics**, it would get the ones for **a single container each time** (for the container that handled that particular request), instead of getting the **accumulated metrics** for all the replicated containers.
|
||||
|
||||
Then, in that case, it could be simpler to have **one container** with **multiple processes**, and a local tool (e.g. a Prometheus exporter) on the same container collecting Prometheus metrics for all the internal processes and exposing those metrics on that single container.
|
||||
|
||||
---
|
||||
|
||||
With this information and tools, continue with the next section to combine everything.
|
||||
The main point is, **none** of these are **rules written in stone** that you have to blindly follow. You can use these ideas to **evaluate your own use case** and decide what is the best approach for your system, checking out how to manage the concepts of:
|
||||
|
||||
## Docker Swarm mode cluster with Traefik and HTTPS
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
You can have a Docker Swarm mode cluster set up in minutes (about 20 min) with a main Traefik handling HTTPS (including certificate acquisition and renewal).
|
||||
## Memory
|
||||
|
||||
By using Docker Swarm mode, you can start with a "cluster" of a single machine (it can even be a $5 USD / month server) and then you can grow as much as you need adding more servers.
|
||||
If you run **a single process per container** you will have a more or less well-defined, stable, and limited amount of memory consumed by each of those containers (more than one if they are replicated).
|
||||
|
||||
To set up a Docker Swarm Mode cluster with Traefik and HTTPS handling, follow this guide:
|
||||
And then you can set those same memory limits and requirements in your configurations for your container management system (for example in **Kubernetes**). That way it will be able to **replicate the containers** in the **available machines** taking into account the amount of memory needed by them, and the amount available in the machines in the cluster.
|
||||
|
||||
### <a href="https://medium.com/@tiangolo/docker-swarm-mode-and-traefik-for-a-https-cluster-20328dba6232" class="external-link" target="_blank">Docker Swarm Mode and Traefik for an HTTPS cluster</a>
|
||||
If your application is **simple**, this will probably **not be a problem**, and you might not need to specify hard memory limits. But if you are **using a lot of memory** (for example with **machine learning** models), you should check how much memory you are consuming and adjust the **number of containers** that runs in **each machine** (and maybe add more machines to your cluster).
|
||||
|
||||
### Deploy a FastAPI application
|
||||
If you run **multiple processes per container** (for example with the official Docker image) you will have to make sure that the number of processes started doesn't **consume more memory** than what is available.
|
||||
|
||||
The easiest way to set everything up, would be using the [**FastAPI** Project Generators](../project-generation.md){.internal-link target=_blank}.
|
||||
## Previous Steps Before Starting and Containers
|
||||
|
||||
It is designed to be integrated with this Docker Swarm cluster with Traefik and HTTPS described above.
|
||||
If you are using containers (e.g. Docker, Kubernetes), then there are two main approaches you can use.
|
||||
|
||||
You can generate a project in about 2 min.
|
||||
### Multiple Containers
|
||||
|
||||
The generated project has instructions to deploy it, doing it takes another 2 min.
|
||||
If you have **multiple containers**, probably each one running a **single process** (for example, in a **Kubernetes** cluster), then you would probably want to have a **separate container** doing the work of the **previous steps** in a single container, running a single process, **before** running the replicated worker containers.
|
||||
|
||||
!!! info
|
||||
If you are using Kubernetes, this would probably be an <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>.
|
||||
|
||||
If in your use case there's no problem in running those previous steps **multiple times in parallel** (for example if you are not running database migrations, but just checking if the database is ready yet), then you could also just put them in each container right before starting the main process.
|
||||
|
||||
### Single Container
|
||||
|
||||
If you have a simple setup, with a **single container** that then starts multiple **worker processes** (or also just one process), then you could run those previous steps in the same container, right before starting the process with the app. The official Docker image supports this internally.
|
||||
|
||||
## Official Docker Image with Gunicorn - Uvicorn
|
||||
|
||||
There is an official Docker image that includes Gunicorn running with Uvicorn workers, as detailed in a previous chapter: [Server Workers - Gunicorn with Uvicorn](./server-workers.md){.internal-link target=_blank}.
|
||||
|
||||
This image would be useful mainly in the situations described above in: [Containers with Multiple Processes and Special Cases](#containers-with-multiple-processes-and-special-cases).
|
||||
|
||||
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
!!! warning
|
||||
There's a high chance that you **don't** need this base image or any other similar one, and would be better off by building the image from scratch as [described above in: Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi).
|
||||
|
||||
This image has an **auto-tuning** mechanism included to set the **number of worker processes** based on the CPU cores available.
|
||||
|
||||
It has **sensible defaults**, but you can still change and update all the configurations with **environment variables** or configuration files.
|
||||
|
||||
It also supports running <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**previous steps before starting**</a> with a script.
|
||||
|
||||
!!! tip
|
||||
To see all the configurations and options, go to the Docker image page: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
### Number of Processes on the Official Docker Image
|
||||
|
||||
The **number of processes** on this image is **computed automatically** from the CPU **cores** available.
|
||||
|
||||
This means that it will try to **squeeze** as much **performance** from the CPU as possible.
|
||||
|
||||
You can also adjust it with the configurations using **environment variables**, etc.
|
||||
|
||||
But it also means that as the number of processes depends on the CPU the container is running, the **amount of memory consumed** will also depend on that.
|
||||
|
||||
So, if your application consumes a lot of memory (for example with machine learning models), and your server has a lot of CPU cores **but little memory**, then your container could end up trying to use more memory than what is available, and degrading performance a lot (or even crashing). 🚨
|
||||
|
||||
### Create a `Dockerfile`
|
||||
|
||||
Here's how you would create a `Dockerfile` based on this image:
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./app /app
|
||||
```
|
||||
|
||||
### Bigger Applications
|
||||
|
||||
If you followed the section about creating [Bigger Applications with Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}, your `Dockerfile` might instead look like:
|
||||
|
||||
```Dockerfile hl_lines="7"
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./app /app/app
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
You should probably **not** use this official base image (or any other similar one) if you are using **Kubernetes** (or others) and you are already setting **replication** at the cluster level, with multiple **containers**. In those cases, you are better off **building an image from scratch** as described above: [Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi).
|
||||
|
||||
This image would be useful mainly in the special cases described above in [Containers with Multiple Processes and Special Cases](#containers-with-multiple-processes-and-special-cases). For example, if your application is **simple enough** that setting a default number of processes based on the CPU works well, you don't want to bother with manually configuring the replication at the cluster level, and you are not running more than one container with your app. Or if you are deploying with **Docker Compose**, running on a single server, etc.
|
||||
|
||||
## Deploy the Container Image
|
||||
|
||||
After having a Container (Docker) Image there are several ways to deploy it.
|
||||
|
||||
For example:
|
||||
|
||||
* With **Docker Compose** in a single server
|
||||
* With a **Kubernetes** cluster
|
||||
* With a Docker Swarm Mode cluster
|
||||
* With another tool like Nomad
|
||||
* With a cloud service that takes your container image and deploys it
|
||||
|
||||
## Docker Image with Poetry
|
||||
|
||||
If you use <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to manage your project's dependencies, you could use Docker multi-stage building:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
FROM python:3.9 as requirements-stage
|
||||
|
||||
# (2)
|
||||
WORKDIR /tmp
|
||||
|
||||
# (3)
|
||||
RUN pip install poetry
|
||||
|
||||
# (4)
|
||||
COPY ./pyproject.toml ./poetry.lock* /tmp/
|
||||
|
||||
# (5)
|
||||
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
|
||||
|
||||
# (6)
|
||||
FROM python:3.9
|
||||
|
||||
# (7)
|
||||
WORKDIR /code
|
||||
|
||||
# (8)
|
||||
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
|
||||
|
||||
# (9)
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (10)
|
||||
COPY ./app /code/app
|
||||
|
||||
# (11)
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
1. This is the first stage, it is named `requirements-stage`.
|
||||
|
||||
2. Set `/tmp` as the current working directory.
|
||||
|
||||
Here's where we will generate the file `requirements.txt`
|
||||
|
||||
3. Install Poetry in this Docker stage.
|
||||
|
||||
4. Copy the `pyproject.toml` and `poetry.lock` files to the `/tmp` directory.
|
||||
|
||||
Because it uses `./poetry.lock*` (ending with a `*`), it won't crash if that file is not available yet.
|
||||
|
||||
5. Generate the `requirements.txt` file.
|
||||
|
||||
6. This is the final stage, anything here will be preserved in the final container image.
|
||||
|
||||
7. Set the current working directory to `/code`.
|
||||
|
||||
8. Copy the `requirements.txt` file to the `/code` directory.
|
||||
|
||||
This file only lives in the previous Docker stage, that's why we use `--from-requirements-stage` to copy it.
|
||||
|
||||
9. Install the package dependencies in the generated `requirements.txt` file.
|
||||
|
||||
10. Copy the `app` directory to the `/code` directory.
|
||||
|
||||
11. Run the `uvicorn` command, telling it to use the `app` object imported from `app.main`.
|
||||
|
||||
!!! tip
|
||||
Click the bubble numbers to see what each line does.
|
||||
|
||||
A **Docker stage** is a part of a `Dockerfile` that works as a **temporary container image** that is only used to generate some files to be used later.
|
||||
|
||||
The first stage will only be used to **install Poetry** and to **generate the `requirements.txt`** with your project dependencies from Poetry's `pyproject.toml` file.
|
||||
|
||||
This `requirements.txt` file will be used with `pip` later in the **next stage**.
|
||||
|
||||
In the final container image **only the final stage** is preserved. The previous stage(s) will be discarded.
|
||||
|
||||
When using Poetry, it would make sense to use **Docker multi-stage builds** because you don't really need to have Poetry and its dependencies installed in the final container image, you **only need** to have the generated `requirements.txt` file to install your project dependencies.
|
||||
|
||||
Then in the next (and final) stage you would build the image more or less in the same way as described before.
|
||||
|
||||
### Behind a TLS Termination Proxy - Poetry
|
||||
|
||||
Again, if you are running your container behind a TLS Termination Proxy (load balancer) like Nginx or Traefik, add the option `--proxy-headers` to the command:
|
||||
|
||||
```Dockerfile
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
Using container systems (e.g. with **Docker** and **Kubernetes**) it becomes fairly straightforward to handle all the **deployment concepts**:
|
||||
|
||||
* HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
In most cases, you probably won't want to use any base image, and instead **build a container image from scratch** one based on the official Python Docker image.
|
||||
|
||||
Taking care of the **order** of instructions in the `Dockerfile` and the **Docker cache** you can **minimize build times**, to maximize your productivity (and avoid boredom). 😎
|
||||
|
||||
In certain special cases, you might want to use the official Docker image for FastAPI. 🤓
|
||||
|
||||
@@ -7,42 +7,184 @@ But it is way more complex than that.
|
||||
!!! tip
|
||||
If you are in a hurry or don't care, continue with the next sections for step by step instructions to set everything up with different techniques.
|
||||
|
||||
To learn the basics of HTTPS, from a consumer perspective, check <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
To **learn the basics of HTTPS**, from a consumer perspective, check <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
|
||||
Now, from a developer's perspective, here are several things to have in mind while thinking about HTTPS:
|
||||
Now, from a **developer's perspective**, here are several things to have in mind while thinking about HTTPS:
|
||||
|
||||
* For HTTPS, the server needs to have "certificates" generated by a third party.
|
||||
* Those certificates are actually acquired from the third-party, not "generated".
|
||||
* Certificates have a lifetime.
|
||||
* They expire.
|
||||
* And then they need to be renewed, acquired again from the third party.
|
||||
* The encryption of the connection happens at the TCP level.
|
||||
* That's one layer below HTTP.
|
||||
* So, the certificate and encryption handling is done before HTTP.
|
||||
* TCP doesn't know about "domains". Only about IP addresses.
|
||||
* The information about the specific domain requested goes in the HTTP data.
|
||||
* The HTTPS certificates "certify" a certain domain, but the protocol and encryption happen at the TCP level, before knowing which domain is being dealt with.
|
||||
* By default, that would mean that you can only have one HTTPS certificate per IP address.
|
||||
* For HTTPS, **the server** needs to **have "certificates"** generated by a **third party**.
|
||||
* Those certificates are actually **acquired** from the third party, not "generated".
|
||||
* Certificates have a **lifetime**.
|
||||
* They **expire**.
|
||||
* And then they need to be **renewed**, **acquired again** from the third party.
|
||||
* The encryption of the connection happens at the **TCP level**.
|
||||
* That's one layer **below HTTP**.
|
||||
* So, the **certificate and encryption** handling is done **before HTTP**.
|
||||
* **TCP doesn't know about "domains"**. Only about IP addresses.
|
||||
* The information about the **specific domain** requested goes in the **HTTP data**.
|
||||
* The **HTTPS certificates** "certify" a **certain domain**, but the protocol and encryption happen at the TCP level, **before knowing** which domain is being dealt with.
|
||||
* **By default**, that would mean that you can only have **one HTTPS certificate per IP address**.
|
||||
* No matter how big your server is or how small each application you have on it might be.
|
||||
* There is a solution to this, however.
|
||||
* There's an extension to the TLS protocol (the one handling the encryption at the TCP level, before HTTP) called <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>.
|
||||
* This SNI extension allows one single server (with a single IP address) to have several HTTPS certificates and serve multiple HTTPS domains/applications.
|
||||
* For this to work, a single component (program) running on the server, listening on the public IP address, must have all the HTTPS certificates in the server.
|
||||
* After obtaining a secure connection, the communication protocol is still HTTP.
|
||||
* The contents are encrypted, even though they are being sent with the HTTP protocol.
|
||||
* There is a **solution** to this, however.
|
||||
* There's an **extension** to the **TLS** protocol (the one handling the encryption at the TCP level, before HTTP) called **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>**.
|
||||
* This SNI extension allows one single server (with a **single IP address**) to have **several HTTPS certificates** and serve **multiple HTTPS domains/applications**.
|
||||
* For this to work, a **single** component (program) running on the server, listening on the **public IP address**, must have **all the HTTPS certificates** in the server.
|
||||
* **After** obtaining a secure connection, the communication protocol is **still HTTP**.
|
||||
* The contents are **encrypted**, even though they are being sent with the **HTTP protocol**.
|
||||
|
||||
It is a common practice to have one program/HTTP server running on the server (the machine, host, etc.) and managing all the HTTPS parts : sending the decrypted HTTP requests to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the HTTP response from the application, encrypt it using the appropriate certificate and sending it back to the client using HTTPS. This server is often called a <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>.
|
||||
It is a common practice to have **one program/HTTP server** running on the server (the machine, host, etc.) and **managing all the HTTPS parts**: receiving the **encrypted HTTPS requests**, sending the **decrypted HTTP requests** to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the **HTTP response** from the application, **encrypt it** using the appropriate **HTTPS certificate** and sending it back to the client using **HTTPS**. This server is often called a **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>**.
|
||||
|
||||
Some of the options you could use as a TLS Termination Proxy are:
|
||||
|
||||
* Traefik (that can also handle certificate renewals)
|
||||
* Caddy (that can also handle certificate renewals)
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
## Let's Encrypt
|
||||
|
||||
Before Let's Encrypt, these HTTPS certificates were sold by trusted third-parties.
|
||||
Before Let's Encrypt, these **HTTPS certificates** were sold by trusted third parties.
|
||||
|
||||
The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive.
|
||||
|
||||
But then <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> was created.
|
||||
But then **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** was created.
|
||||
|
||||
It is a project from the Linux Foundation. It provides HTTPS certificates for free. In an automated way. These certificates use all the standard cryptographic security, and are short lived (about 3 months), so the security is actually better because of their reduced lifespan.
|
||||
It is a project from the Linux Foundation. It provides **HTTPS certificates for free**, in an automated way. These certificates use all the standard cryptographic security, and are short-lived (about 3 months), so the **security is actually better** because of their reduced lifespan.
|
||||
|
||||
The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates.
|
||||
|
||||
The idea is to automate the acquisition and renewal of these certificates, so that you can have secure HTTPS, for free, forever.
|
||||
The idea is to automate the acquisition and renewal of these certificates so that you can have **secure HTTPS, for free, forever**.
|
||||
|
||||
## HTTPS for Developers
|
||||
|
||||
Here's an example of how an HTTPS API could look like, step by step, paying attention mainly to the ideas important for developers.
|
||||
|
||||
### Domain Name
|
||||
|
||||
It would probably all start by you **acquiring** some **domain name**. Then, you would configure it in a DNS server (possibly your same cloud provider).
|
||||
|
||||
You would probably get a cloud server (a virtual machine) or something similar, and it would have a <abbr title="That doesn't change">fixed</abbr> **public IP address**.
|
||||
|
||||
In the DNS server(s) you would configure a record (an "`A record`") to point **your domain** to the public **IP address of your server**.
|
||||
|
||||
You would probably do this just once, the first time, when setting everything up.
|
||||
|
||||
!!! tip
|
||||
This Domain Name part is way before HTTPS, but as everything depends on the domain and the IP address, it's worth mentioning it here.
|
||||
|
||||
### DNS
|
||||
|
||||
Now let's focus on all the actual HTTPS parts.
|
||||
|
||||
First, the browser would check with the **DNS servers** what is the **IP for the domain**, in this case, `someapp.example.com`.
|
||||
|
||||
The DNS servers would tell the browser to use some specific **IP address**. That would be the public IP address used by your server, that you configured in the DNS servers.
|
||||
|
||||
<img src="/img/deployment/https/https01.svg">
|
||||
|
||||
### TLS Handshake Start
|
||||
|
||||
The browser would then communicate with that IP address on **port 443** (the HTTPS port).
|
||||
|
||||
The first part of the communication is just to establish the connection between the client and the server and to decide the cryptographic keys they will use, etc.
|
||||
|
||||
<img src="/img/deployment/https/https02.svg">
|
||||
|
||||
This interaction between the client and the server to establish the TLS connection is called the **TLS handshake**.
|
||||
|
||||
### TLS with SNI Extension
|
||||
|
||||
**Only one process** in the server can be listening on a specific **port** in a specific **IP address**. There could be other processes listening on other ports in the same IP address, but only one for each combination of IP address and port.
|
||||
|
||||
TLS (HTTPS) uses the specific port `443` by default. So that's the port we would need.
|
||||
|
||||
As only one process can be listening on this port, the process that would do it would be the **TLS Termination Proxy**.
|
||||
|
||||
The TLS Termination Proxy would have access to one or more **TLS certificates** (HTTPS certificates).
|
||||
|
||||
Using the **SNI extension** discussed above, the TLS Termination Proxy would check which of the TLS (HTTPS) certificates available it should use for this connection, using the one that matches the domain expected by the client.
|
||||
|
||||
In this case, it would use the certificate for `someapp.example.com`.
|
||||
|
||||
<img src="/img/deployment/https/https03.svg">
|
||||
|
||||
The client already **trusts** the entity that generated that TLS certificate (in this case Let's Encrypt, but we'll see about that later), so it can **verify** that the certificate is valid.
|
||||
|
||||
Then, using the certificate, the client and the TLS Termination Proxy **decide how to encrypt** the rest of the **TCP communication**. This completes the **TLS Handshake** part.
|
||||
|
||||
After this, the client and the server have an **encrypted TCP connection**, this is what TLS provides. And then they can use that connection to start the actual **HTTP communication**.
|
||||
|
||||
And that's what **HTTPS** is, it's just plain **HTTP** inside a **secure TLS connection** instead of a pure (unencrypted) TCP connection.
|
||||
|
||||
!!! tip
|
||||
Notice that the encryption of the communication happens at the **TCP level**, not at the HTTP level.
|
||||
|
||||
### HTTPS Request
|
||||
|
||||
Now that the client and server (specifically the browser and the TLS Termination Proxy) have an **encrypted TCP connection**, they can start the **HTTP communication**.
|
||||
|
||||
So, the client sends an **HTTPS request**. This is just an HTTP request through an encrypted TLS connection.
|
||||
|
||||
<img src="/img/deployment/https/https04.svg">
|
||||
|
||||
### Decrypt the Request
|
||||
|
||||
The TLS Termination Proxy would use the encryption agreed to **decrypt the request**, and would transmit the **plain (decrypted) HTTP request** to the process running the application (for example a process with Uvicorn running the FastAPI application).
|
||||
|
||||
<img src="/img/deployment/https/https05.svg">
|
||||
|
||||
### HTTP Response
|
||||
|
||||
The application would process the request and send a **plain (unencrypted) HTTP response** to the TLS Termination Proxy.
|
||||
|
||||
<img src="/img/deployment/https/https06.svg">
|
||||
|
||||
### HTTPS Response
|
||||
|
||||
The TLS Termination Proxy would then **encrypt the response** using the cryptography agreed before (that started with the certificate for `someapp.example.com`), and send it back to the browser.
|
||||
|
||||
Next, the browser would verify that the response is valid and encrypted with the right cryptographic key, etc. It would then **decrypt the response** and process it.
|
||||
|
||||
<img src="/img/deployment/https/https07.svg">
|
||||
|
||||
The client (browser) will know that the response comes from the correct server because it is using the cryptography they agreed using the **HTTPS certificate** before.
|
||||
|
||||
### Multiple Applications
|
||||
|
||||
In the same server (or servers), there could be **multiple applications**, for example, other API programs or a database.
|
||||
|
||||
Only one process can be handling the specific IP and port (the TLS Termination Proxy in our example) but the other applications/processes can be running on the server(s) too, as long as they don't try to use the same **combination of public IP and port**.
|
||||
|
||||
<img src="/img/deployment/https/https08.svg">
|
||||
|
||||
That way, the TLS Termination Proxy could handle HTTPS and certificates for **multiple domains**, for multiple applications, and then transmit the requests to the right application in each case.
|
||||
|
||||
### Certificate Renewal
|
||||
|
||||
At some point in the future, each certificate would **expire** (about 3 months after acquiring it).
|
||||
|
||||
And then, there would be another program (in some cases it's another program, in some cases it could be the same TLS Termination Proxy) that would talk to Let's Encrypt, and renew the certificate(s).
|
||||
|
||||
<img src="/img/deployment/https/https.svg">
|
||||
|
||||
The **TLS certificates** are **associated with a domain name**, not with an IP address.
|
||||
|
||||
So, to renew the certificates, the renewal program needs to **prove** to the authority (Let's Encrypt) that it indeed **"owns" and controls that domain**.
|
||||
|
||||
To do that, and to accommodate different application needs, there are several ways it can do it. Some popular ways are:
|
||||
|
||||
* **Modify some DNS records**.
|
||||
* For this, the renewal program needs to support the APIs of the DNS provider, so, depending on the DNS provider you are using, this might or might not be an option.
|
||||
* **Run as a server** (at least during the certificate acquisition process) on the public IP address associated with the domain.
|
||||
* As we said above, only one process can be listening on a specific IP and port.
|
||||
* This is one of the reasons why it's very useful when the same TLS Termination Proxy also takes care of the certificate renewal process.
|
||||
* Otherwise, you might have to stop the TLS Termination Proxy momentarily, start the renewal program to acquire the certificates, then configure them with the TLS Termination Proxy, and then restart the TLS Termination Proxy. This is not ideal, as your app(s) will not be available during the time that the TLS Termination Proxy is off.
|
||||
|
||||
All this renewal process, while still serving the app, is one of the main reasons why you would want to have a **separate system to handle HTTPS** with a TLS Termination Proxy instead of just using the TLS certificates with the application server directly (e.g. Uvicorn).
|
||||
|
||||
## Recap
|
||||
|
||||
Having **HTTPS** is very important, and quite **critical** in most cases. Most of the effort you as a developer have to put around HTTPS is just about **understanding these concepts** and how they work.
|
||||
|
||||
But once you know the basic information of **HTTPS for developers** you can easily combine and configure different tools to help you manage everything in a simple way.
|
||||
|
||||
In some of the next chapters, I'll show you several concrete examples of how to set up **HTTPS** for **FastAPI** applications. 🔒
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
|
||||
Deploying a **FastAPI** application is relatively easy.
|
||||
|
||||
## What Does Deployment Mean
|
||||
|
||||
To **deploy** an application means to perform the necessary steps to make it **available to the users**.
|
||||
|
||||
For a **web API**, it normally involves putting it in a **remote machine**, with a **server program** that provides good performance, stability, etc, so that your **users** can **access** the application efficiently and without interruptions or problems.
|
||||
|
||||
This is in contrast to the **development** stages, where you are constantly changing the code, breaking it and fixing it, stopping and restarting the development server, etc.
|
||||
|
||||
## Deployment Strategies
|
||||
|
||||
There are several ways to do it depending on your specific use case and the tools that you use.
|
||||
|
||||
You will see more details to have in mind and some of the techniques to do it in the next sections.
|
||||
You could **deploy a server** yourself using a combination of tools, you could use a **cloud service** that does part of the work for you, or other possible options.
|
||||
|
||||
I will show you some of the main concepts you should probably have in mind when deploying a **FastAPI** application (although most of it applies to any other type of web application).
|
||||
|
||||
You will see more details to have in mind and some of the techniques to do it in the next sections. ✨
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
# Deploy manually
|
||||
# Run a Server Manually - Uvicorn
|
||||
|
||||
You can deploy **FastAPI** manually as well.
|
||||
The main thing you need to run a **FastAPI** application in a remote server machine is an ASGI server program like **Uvicorn**.
|
||||
|
||||
You just need to install an ASGI compatible server like:
|
||||
There are 3 main alternatives:
|
||||
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: a high performance ASGI server.
|
||||
* <a href="https://pgjones.gitlab.io/hypercorn/" class="external-link" target="_blank">Hypercorn</a>: an ASGI server compatible with HTTP/2 and Trio among other features.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: the ASGI server built for Django Channels.
|
||||
|
||||
## Server Machine and Server Program
|
||||
|
||||
There's a small detail about names to have in mind. 💡
|
||||
|
||||
The word "**server**" is commonly used to refer to both the remote/cloud computer (the physical or virtual machine) and also the program that is running on that machine (e.g. Uvicorn).
|
||||
|
||||
Just have that in mind when you read "server" in general, it could refer to one of those two things.
|
||||
|
||||
When referring to the remote machine, it's common to call it **server**, but also **machine**, **VM** (virtual machine), **node**. Those all refer to some type of remote machine, normally running Linux, where you run programs.
|
||||
|
||||
## Install the Server Program
|
||||
|
||||
You can install an ASGI compatible server with:
|
||||
|
||||
=== "Uvicorn"
|
||||
|
||||
@@ -11,7 +29,7 @@ You just need to install an ASGI compatible server like:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install uvicorn[standard]
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
@@ -39,7 +57,9 @@ You just need to install an ASGI compatible server like:
|
||||
|
||||
...or any other ASGI server.
|
||||
|
||||
And run your application the same way you have done in the tutorials, but without the `--reload` option, e.g.:
|
||||
## Run the Server Program
|
||||
|
||||
You can then your application the same way you have done in the tutorials, but without the `--reload` option, e.g.:
|
||||
|
||||
=== "Uvicorn"
|
||||
|
||||
@@ -65,10 +85,61 @@ And run your application the same way you have done in the tutorials, but withou
|
||||
|
||||
</div>
|
||||
|
||||
You might want to set up some tooling to make sure it is restarted automatically if it stops.
|
||||
!!! warning
|
||||
Remember to remove the `--reload` option if you were using it.
|
||||
|
||||
You might also want to install <a href="https://gunicorn.org/" class="external-link" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" class="external-link" target="_blank">use it as a manager for Uvicorn</a>, or use Hypercorn with multiple workers.
|
||||
The `--reload` option consumes much more resources, is more unstable, etc.
|
||||
|
||||
It helps a lot during **development**, but you **shouldn't** use it in **production**.
|
||||
|
||||
Making sure to fine-tune the number of workers, etc.
|
||||
## Hypercorn with Trio
|
||||
|
||||
But if you are doing all that, you might just use the Docker image that does it automatically.
|
||||
Starlette and **FastAPI** are based on <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, which makes them compatible with both Python's standard library <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> and <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
|
||||
Nevertheless, Uvicorn is currently only compatible with asyncio, and it normally uses <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>, the high-performance drop-in replacement for `asyncio`.
|
||||
|
||||
But if you want to directly use **Trio**, then you can use **Hypercorn** as it supports it. ✨
|
||||
|
||||
### Install Hypercorn with Trio
|
||||
|
||||
First you need to install Hypercorn with Trio support:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "hypercorn[trio]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Run with Trio
|
||||
|
||||
Then you can pass the command line option `--worker-class` with the value `trio`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --worker-class trio
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
And that will start Hypercorn with your app using Trio as the backend.
|
||||
|
||||
Now you can use Trio internally in your app. Or even better, you can use AnyIO, to keep your code compatible with both Trio and asyncio. 🎉
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
These examples run the server program (e.g Uvicorn), starting **a single process**, listening on all the IPs (`0.0.0.0`) on a predefined port (e.g. `80`).
|
||||
|
||||
This is the basic idea. But you will probably want to take care of some additional things, like:
|
||||
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
I'll tell you more about each of these concepts, how to think about them, and some concrete examples with strategies to handle them in the next chapters. 🚀
|
||||
|
||||
178
docs/en/docs/deployment/server-workers.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# Server Workers - Gunicorn with Uvicorn
|
||||
|
||||
Let's check back those deployment concepts from before:
|
||||
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* **Replication (the number of processes running)**
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
Up to this point, with all the tutorials in the docs, you have probably been running a **server program** like Uvicorn, running a **single process**.
|
||||
|
||||
When deploying applications you will probably want to have some **replication of processes** to take advantage of **multiple cores** and to be able to handle more requests.
|
||||
|
||||
As you saw in the previous chapter about [Deployment Concepts](./concepts.md){.internal-link target=_blank}, there are multiple strategies you can use.
|
||||
|
||||
Here I'll show you how to use <a href="https://gunicorn.org/" class="external-link" target="_blank">**Gunicorn**</a> with **Uvicorn worker processes**.
|
||||
|
||||
!!! info
|
||||
If you are using containers, for example with Docker or Kubernetes, I'll tell you more about that in the next chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}.
|
||||
|
||||
In particular, when running on **Kubernetes** you will probably **not** want to use Gunicorn and instead run **a single Uvicorn process per container**, but I'll tell you about it later in that chapter.
|
||||
|
||||
## Gunicorn with Uvicorn Workers
|
||||
|
||||
**Gunicorn** is mainly an application server using the **WSGI standard**. That means that Gunicorn can serve applications like Flask and Django. Gunicorn by itself is not compatible with **FastAPI**, as FastAPI uses the newest **<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI standard</a>**.
|
||||
|
||||
But Gunicorn supports working as a **process manager** and allowing users to tell it which specific **worker process class** to use. Then Gunicorn would start one or more **worker processes** using that class.
|
||||
|
||||
And **Uvicorn** has a **Gunicorn-compatible worker class**.
|
||||
|
||||
Using that combination, Gunicorn would act as a **process manager**, listening on the **port** and the **IP**. And it would **transmit** the communication to the worker processes running the **Uvicorn class**.
|
||||
|
||||
And then the Gunicorn-compatible **Uvicorn worker** class would be in charge of converting the data sent by Gunicorn to the ASGI standard for FastAPI to use it.
|
||||
|
||||
## Install Gunicorn and Uvicorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]" gunicorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
That will install both Uvicorn with the `standard` extra packages (to get high performance) and Gunicorn.
|
||||
|
||||
## Run Gunicorn with Uvicorn Workers
|
||||
|
||||
Then you can run Gunicorn with:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
|
||||
|
||||
[19499] [INFO] Starting gunicorn 20.1.0
|
||||
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
|
||||
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
|
||||
[19511] [INFO] Booting worker with pid: 19511
|
||||
[19513] [INFO] Booting worker with pid: 19513
|
||||
[19514] [INFO] Booting worker with pid: 19514
|
||||
[19515] [INFO] Booting worker with pid: 19515
|
||||
[19511] [INFO] Started server process [19511]
|
||||
[19511] [INFO] Waiting for application startup.
|
||||
[19511] [INFO] Application startup complete.
|
||||
[19513] [INFO] Started server process [19513]
|
||||
[19513] [INFO] Waiting for application startup.
|
||||
[19513] [INFO] Application startup complete.
|
||||
[19514] [INFO] Started server process [19514]
|
||||
[19514] [INFO] Waiting for application startup.
|
||||
[19514] [INFO] Application startup complete.
|
||||
[19515] [INFO] Started server process [19515]
|
||||
[19515] [INFO] Waiting for application startup.
|
||||
[19515] [INFO] Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Let's see what each of those options mean:
|
||||
|
||||
* `main:app`: This is the same syntax used by Uvicorn, `main` means the Python module named "`main`", so, a file `main.py`. And `app` is the name of the variable that is the **FastAPI** application.
|
||||
* You can imagine that `main:app` is equivalent to a Python `import` statement like:
|
||||
|
||||
```Python
|
||||
from main import app
|
||||
```
|
||||
|
||||
* So, the colon in `main:app` would be equivalent to the Python `import` part in `from main import app`.
|
||||
* `--workers`: The number of worker processes to use, each will run a Uvicorn worker, in this case, 4 workers.
|
||||
* `--worker-class`: The Gunicorn-compatible worker class to use in the worker processes.
|
||||
* Here we pass the class that Gunicorn can import and use with:
|
||||
|
||||
```Python
|
||||
import uvicorn.workers.UvicornWorker
|
||||
```
|
||||
|
||||
* `--bind`: This tells Gunicorn the IP and the port to listen to, using a colon (`:`) to separate the IP and the port.
|
||||
* If you were running Uvicorn directly, instead of `--bind 0.0.0.0:80` (the Gunicorn option) you would use `--host 0.0.0.0` and `--port 80`.
|
||||
|
||||
In the output, you can see that it shows the **PID** (process ID) of each process (it's just a number).
|
||||
|
||||
You can see that:
|
||||
|
||||
* The Gunicorn **process manager** starts with PID `19499` (in your case it will be a different number).
|
||||
* Then it starts `Listening at: http://0.0.0.0:80`.
|
||||
* Then it detects that it has to use the worker class at `uvicorn.workers.UvicornWorker`.
|
||||
* And then it starts **4 workers**, each with its own PID: `19511`, `19513`, `19514`, and `19515`.
|
||||
|
||||
Gunicorn would also take care of managing **dead processes** and **restarting** new ones if needed to keep the number of workers. So that helps in part with the **restart** concept from the list above.
|
||||
|
||||
Nevertheless, you would probably also want to have something outside making sure to **restart Gunicorn** if necessary, and also to **run it on startup**, etc.
|
||||
|
||||
## Uvicorn with Workers
|
||||
|
||||
Uvicorn also has an option to start and run several **worker processes**.
|
||||
|
||||
Nevertheless, as of now, Uvicorn's capabilities for handling worker processes are more limited than Gunicorn's. So, if you want to have a process manager at this level (at the Python level), then it might be better to try with Gunicorn as the process manager.
|
||||
|
||||
In any case, you would run it like this:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
|
||||
<font color="#A6E22E">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
|
||||
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The only new option here is `--workers` telling Uvicorn to start 4 worker processes.
|
||||
|
||||
You can also see that it shows the **PID** of each process, `27365` for the parent process (this is the **process manager**) and one for each worker process: `27368`, `27369`, `27370`, and `27367`.
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
Here you saw how to use **Gunicorn** (or Uvicorn) managing **Uvicorn worker processes** to **parallelize** the execution of the application, take advantage of **multiple cores** in the CPU, and be able to serve **more requests**.
|
||||
|
||||
From the list of deployment concepts from above, using workers would mainly help with the **replication** part, and a little bit with the **restarts**, but you still need to take care of the others:
|
||||
|
||||
* **Security - HTTPS**
|
||||
* **Running on startup**
|
||||
* ***Restarts***
|
||||
* Replication (the number of processes running)
|
||||
* **Memory**
|
||||
* **Previous steps before starting**
|
||||
|
||||
## Containers and Docker
|
||||
|
||||
In the next chapter about [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank} I'll tell some strategies you could use to handle the other **deployment concepts**.
|
||||
|
||||
I'll also show you the **official Docker image** that includes **Gunicorn with Uvicorn workers** and some default configurations that can be useful for simple cases.
|
||||
|
||||
There I'll also show you how to **build your own image from scratch** to run a single Uvicorn process (without Gunicorn). It is a simple process and is probably what you would want to do when using a distributed container management system like **Kubernetes**.
|
||||
|
||||
## Recap
|
||||
|
||||
You can use **Gunicorn** (or also Uvicorn) as a process manager with Uvicorn workers to take advantage of **multi-core CPUs**, to run **multiple processes in parallel**.
|
||||
|
||||
You could use these tools and ideas if you are setting up **your own deployment system** while taking care of the other deployment concepts yourself.
|
||||
|
||||
Check out the next chapter to learn about **FastAPI** with containers (e.g. Docker and Kubernetes). You will see that those tools have simple ways to solve the other **deployment concepts** as well. ✨
|
||||
@@ -164,7 +164,6 @@ With **FastAPI** you get all of **Starlette**'s features (as FastAPI is just Sta
|
||||
|
||||
* Seriously impressive performance. It is <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">one of the fastest Python frameworks available, on par with **NodeJS** and **Go**</a>.
|
||||
* **WebSocket** support.
|
||||
* **GraphQL** support.
|
||||
* In-process background tasks.
|
||||
* Startup and shutdown events.
|
||||
* Test client built on `requests`.
|
||||
|
||||
@@ -121,7 +121,7 @@ Have in mind that as chats allow more "free conversation", it's easy to ask ques
|
||||
|
||||
In GitHub issues the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat systems. 😅
|
||||
|
||||
Conversations in the chat systems are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation. And only the ones in GitHub issues count to become a [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}, so you will most probably receive more attention in GitHub isssues.
|
||||
Conversations in the chat systems are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation. And only the ones in GitHub issues count to become a [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}, so you will most probably receive more attention in GitHub issues.
|
||||
|
||||
On the other side, there are thousands of users in the chat systems, so there's a high chance you'll find someone to talk to there, almost all the time. 😄
|
||||
|
||||
|
||||
BIN
docs/en/docs/img/deployment/concepts/image01.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
106
docs/en/docs/img/deployment/concepts/process-ram.drawio
Normal file
@@ -0,0 +1,106 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="BkDNbdtn8_9fWQybnc8v" name="Page-1">
|
||||
<mxGraphModel dx="741" dy="1167" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="420" y="280" width="920" height="670" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="755" y="290" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="1110" y="410" width="190" height="500" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">RAM<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="1166.92" y="420" width="76.16" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="470" y="410" width="250" height="500" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="<font style="font-size: 24px" face="Roboto">CPU<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="554.61" y="420" width="80.77" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" source="11" target="12" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" source="11" target="13" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="820" y="525" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;endArrow=none;endFill=0;" parent="1" source="11" target="17" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="11" target="18" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="<font face="roboto"><span style="font-size: 24px">Process&nbsp;</span></font><span style="font-family: &#34;roboto&#34; ; font-size: 24px">Manager</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
|
||||
<mxGeometry x="780" y="420" width="250" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="12" target="23" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="<font face="roboto"><span style="font-size: 24px">Worker Process</span></font>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="840" y="540" width="240" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="13" target="24" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="13" target="22" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="775" y="710"/>
|
||||
<mxPoint x="775" y="688"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="<font face="roboto"><span style="font-size: 24px">Worker Process</span></font>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
|
||||
<mxGeometry x="840" y="660" width="240" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="16" target="27" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="16" target="30" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="16" value="<font face="roboto"><span style="font-size: 24px">Another Process</span></font>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="780" y="790" width="250" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="458" width="230" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="460" width="150" height="20" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="21" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#fff2cc;strokeColor=#d6b656;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="508" width="230" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="22" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="618" width="230" height="140" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="23" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">1 GB</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="490" width="150" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="24" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">1 GB</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="650" width="150" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="27" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="768" width="230" height="50" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="30" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="810" width="150" height="50" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
59
docs/en/docs/img/deployment/concepts/process-ram.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
277
docs/en/docs/img/deployment/https/https.drawio
Normal file
@@ -0,0 +1,277 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" vertex="1" parent="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" edge="1" parent="1" target="14">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" edge="1" parent="1" target="17">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" vertex="1" parent="33">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" vertex="1" parent="33">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="101" target="32">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" vertex="1" parent="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="56" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;" edge="1" parent="1" source="55" target="49">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="58" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;startArrow=none;" edge="1" parent="1" source="102" target="57">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="410" y="400" as="targetPoint"/>
|
||||
<mxPoint x="585" y="1050" as="sourcePoint"/>
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="55" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">Cert Renovation Program</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="515" y="780" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="59" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;strokeWidth=3;startArrow=none;" edge="1" parent="1" source="103" target="55">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="875" y="1030" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="790" y="930"/>
|
||||
<mxPoint x="790" y="930"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="57" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Let's Encrypt</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="500" y="1150" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="85" target="6">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="62" target="78">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="920" y="770"/>
|
||||
<mxPoint x="920" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="65" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">Another app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: another.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="50" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="66" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">One more app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: onemore.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="180" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="78" value="<font face="Roboto"><span style="font-size: 24px ; font-weight: 400">A Database</span></font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="780" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="80" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;strokeWidth=3;endArrow=none;" edge="1" parent="1" source="57" target="103">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="480" y="1090" as="sourcePoint"/>
|
||||
<mxPoint x="875" y="1110" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="915" y="1250"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="81" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;endArrow=none;" edge="1" parent="1" source="55" target="102">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="525" y="970" as="targetPoint"/>
|
||||
<mxPoint x="550" y="880" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="525" y="930"/>
|
||||
<mxPoint x="525" y="930"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="62" target="85">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="84" target="62">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" edge="1" parent="1" source="100" target="34">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" edge="1" parent="1" source="32" target="100">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="34" target="101">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="97" target="32">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="340" y="480"/>
|
||||
<mxPoint x="340" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="96" target="36">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="32" target="96">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" vertex="1" parent="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="102" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Renew HTTPS cert for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="430" y="960" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="103" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">New HTTPS cert for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="750" y="1070" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" edge="1" parent="1" source="104" target="36">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="32" target="104">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="97" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" vertex="1" parent="1">
|
||||
<mxGeometry x="90" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="36" target="97">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="415" y="680" as="sourcePoint"/>
|
||||
<mxPoint x="110" y="275" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="245" y="710"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" vertex="1" parent="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" vertex="1" parent="1">
|
||||
<mxGeometry x="885" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="111" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="6" target="84">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="850" y="390" as="sourcePoint"/>
|
||||
<mxPoint x="1190" y="700" as="targetPoint"/>
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https.svg
Normal file
|
After Width: | Height: | Size: 40 KiB |
78
docs/en/docs/img/deployment/https/https01.drawio
Normal file
@@ -0,0 +1,78 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2738" dy="2173" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-10" y="-120"/>
|
||||
<mxPoint x="-10" y="-120"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
57
docs/en/docs/img/deployment/https/https01.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
110
docs/en/docs/img/deployment/https/https02.drawio
Normal file
@@ -0,0 +1,110 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2481" dy="1867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
57
docs/en/docs/img/deployment/https/https02.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
131
docs/en/docs/img/deployment/https/https03.drawio
Normal file
@@ -0,0 +1,131 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2481" dy="1867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https03.svg
Normal file
|
After Width: | Height: | Size: 21 KiB |
152
docs/en/docs/img/deployment/https/https04.drawio
Normal file
@@ -0,0 +1,152 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2312" dy="1667" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https04.svg
Normal file
|
After Width: | Height: | Size: 23 KiB |
166
docs/en/docs/img/deployment/https/https05.drawio
Normal file
@@ -0,0 +1,166 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="5190" dy="5090" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="640" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https05.svg
Normal file
|
After Width: | Height: | Size: 26 KiB |
183
docs/en/docs/img/deployment/https/https06.drawio
Normal file
@@ -0,0 +1,183 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="85" target="6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="62" target="85" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https06.svg
Normal file
|
After Width: | Height: | Size: 27 KiB |
203
docs/en/docs/img/deployment/https/https07.drawio
Normal file
@@ -0,0 +1,203 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="85" target="6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="62" target="85" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="97" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="340" y="480"/>
|
||||
<mxPoint x="340" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="97" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="90" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="36" target="97" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="415" y="680" as="sourcePoint"/>
|
||||
<mxPoint x="110" y="275" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="245" y="710"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https07.svg
Normal file
|
After Width: | Height: | Size: 29 KiB |
217
docs/en/docs/img/deployment/https/https08.drawio
Normal file
@@ -0,0 +1,217 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="85" target="6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;entryX=0.073;entryY=0.01;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.075;exitY=0.998;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="62" target="78" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="917" y="754" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="65" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">Another app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: another.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="50" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="66" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">One more app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: onemore.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="180" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="78" value="<font face="Roboto"><span style="font-size: 24px ; font-weight: 400">A Database</span></font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="780" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="62" target="85" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="97" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="340" y="480"/>
|
||||
<mxPoint x="340" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="97" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="90" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="36" target="97" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="415" y="680" as="sourcePoint"/>
|
||||
<mxPoint x="110" y="275" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="245" y="710"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https08.svg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/en/docs/img/sponsors/calmcode.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
1375
docs/en/docs/img/sponsors/cryptapi-banner.svg
Normal file
|
After Width: | Height: | Size: 131 KiB |
1216
docs/en/docs/img/sponsors/cryptapi.svg
Normal file
|
After Width: | Height: | Size: 108 KiB |
192
docs/en/docs/img/sponsors/haystack-fastapi.svg
Normal file
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="240"
|
||||
height="100"
|
||||
viewBox="0 0 240 100"
|
||||
version="1.1"
|
||||
id="svg5">
|
||||
<metadata
|
||||
id="metadata44">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="14.514768"
|
||||
y="110.37975"
|
||||
width="255.52743"
|
||||
height="29.367089"
|
||||
id="rect25873" />
|
||||
<style
|
||||
id="style51">.cls-1{fill:#03af9d;}.cls-2{fill:#fff;}.cls-3{fill:#2b2f55;}.cls-4,.cls-5{fill:#a0a0c0;}.cls-5{fill-opacity:0.3;}</style>
|
||||
</defs>
|
||||
<g
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke-width:7.0842;stop-color:#000000"
|
||||
id="rect869"
|
||||
width="240"
|
||||
height="100"
|
||||
x="0"
|
||||
y="0" />
|
||||
<g
|
||||
id="g77"
|
||||
transform="matrix(1.21,0,0,1.21,-32.437213,-33.51152)">
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M 40.56,37 H 70.44 A 3.56,3.56 0 0 1 74,40.56 V 80.44 A 3.56,3.56 0 0 1 70.44,84 H 40.56 A 3.56,3.56 0 0 1 37,80.44 V 40.56 A 3.56,3.56 0 0 1 40.56,37 Z"
|
||||
id="path55" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 44,55.52 a 11.5,11.5 0 1 1 23,0 v 17 A 0.5,0.5 0 0 1 66.5,73.02 4.5,4.5 0 0 1 62,68.5 v -13 a 6.5,6.5 0 1 0 -13,0 v 5 a 0.49,0.49 0 0 0 0.49,0.5 h 3 A 0.5,0.5 0 0 0 53,60.5 v -4 a 2.5,2.5 0 0 1 5,0 v 20 A 0.51,0.51 0 0 1 57.48,77 4.48,4.48 0 0 1 53,72.5 v -6 A 0.51,0.51 0 0 0 52.49,66 h -3 A 0.49,0.49 0 0 0 49,66.5 v 2 A 4.5,4.5 0 0 1 44.5,73 0.5,0.5 0 0 1 44,72.5 Z"
|
||||
id="path57" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 98.91,48.18 a 4.9,4.9 0 0 0 -4.53,2.63 v -8.69 a 0.43,0.43 0 0 0 -0.46,-0.46 h -3.3 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 h 3.3 a 0.43,0.43 0 0 0 0.46,-0.46 v -7.81 c 0,-2.11 1.16,-3.24 2.94,-3.24 1.78,0 2.78,1.13 2.78,3.24 v 7.77 a 0.44,0.44 0 0 0 0.46,0.46 h 3.28 a 0.46,0.46 0 0 0 0.49,-0.46 v -8.72 c 0,-3.92 -2.33,-6.18 -5.42,-6.18 z m 14.78,15.73 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 118.69,49 l -0.06,1.65 a 5.49,5.49 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.87,7.88 7.09,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.79,4 z m 17,9.3 a 0.56,0.56 0 0 0 0.55,-0.36 l 7.92,-19.92 c 0.13,-0.34 0,-0.52 -0.36,-0.52 h -3.37 a 0.56,0.56 0 0 0 -0.55,0.36 l -3.55,9.73 -3.58,-9.73 a 0.56,0.56 0 0 0 -0.55,-0.36 h -3.37 c -0.33,0 -0.49,0.18 -0.36,0.52 l 5.78,14.5 -2.14,5.26 c -0.16,0.34 0,0.52 0.36,0.52 z m 15.09,-5.44 c 3,0 5.6,-1.56 5.63,-4.59 0,-2.3 -1.47,-3.58 -3.36,-4.35 l -2.12,-0.82 c -0.82,-0.31 -1.43,-0.74 -1.43,-1.47 0,-0.73 0.45,-1.2 1.43,-1.2 a 4.3,4.3 0 0 1 2.73,1.26 c 0.27,0.21 0.49,0.24 0.7,0 l 1.35,-1.62 a 0.49,0.49 0 0 0 0,-0.65 6.44,6.44 0 0 0 -5,-2.29 c -2.78,0 -5.23,1.65 -5.23,4.53 0,2.08 1.34,3.33 3.27,4.1 l 1.9,0.76 c 1.1,0.46 1.56,0.83 1.56,1.56 0,0.73 -0.67,1.26 -1.68,1.26 a 5.7,5.7 0 0 1 -3.42,-1.47 0.47,0.47 0 0 0 -0.77,0.12 l -1.07,1.5 a 0.76,0.76 0 0 0 -0.06,0.89 7.16,7.16 0 0 0 5.62,2.48 z m 14,0 c 1,0 2.54,-0.19 2.54,-0.89 v -2.23 c 0,-0.31 -0.22,-0.46 -0.55,-0.43 -0.33,0.03 -0.68,0 -0.95,0 a 1.2,1.2 0 0 1 -1.32,-1.32 v -7.22 h 2.36 a 0.44,0.44 0 0 0 0.46,-0.46 V 49 a 0.44,0.44 0 0 0 -0.46,-0.46 h -2.36 V 45 a 0.43,0.43 0 0 0 -0.46,-0.46 H 156.6 A 0.43,0.43 0 0 0 156.14,45 v 3.52 h -1.9 a 0.43,0.43 0 0 0 -0.46,0.46 v 2.35 a 0.43,0.43 0 0 0 0.46,0.46 h 1.9 v 7.71 c 0.06,3.31 2.2,4.41 4.59,4.41 z m 11.45,0 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 177.12,49 l -0.06,1.65 a 5.48,5.48 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.97,7.88 7.15,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.73,4 z m 18.79,3.86 a 7.17,7.17 0 0 0 5.76,-2.7 0.44,0.44 0 0 0 -0.07,-0.64 l -1.83,-1.8 a 0.54,0.54 0 0 0 -0.8,0 3.85,3.85 0 0 1 -2.81,1.23 3.9,3.9 0 0 1 -4,-4 3.78,3.78 0 0 1 3.95,-3.93 3.85,3.85 0 0 1 2.94,1.25 0.56,0.56 0 0 0 0.8,0 l 1.83,-1.81 a 0.43,0.43 0 0 0 0.06,-0.64 7.19,7.19 0 0 0 -5.81,-2.69 7.63,7.63 0 0 0 -7.83,7.8 7.74,7.74 0 0 0 7.87,7.93 z m 12.5,-0.37 a 0.43,0.43 0 0 0 0.46,-0.46 V 59.9 l 1.87,-2.11 4,5.47 a 0.57,0.57 0 0 0 0.55,0.28 h 3.55 c 0.37,0 0.49,-0.24 0.27,-0.55 l -5.87,-8.11 5.08,-5.78 c 0.24,-0.31 0.15,-0.55 -0.25,-0.55 h -3.66 a 0.67,0.67 0 0 0 -0.55,0.24 l -4.93,5.81 V 42.12 a 0.43,0.43 0 0 0 -0.46,-0.46 H 201 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 z"
|
||||
id="path59" />
|
||||
<path
|
||||
class="cls-3"
|
||||
d="m 98.91,48.18 a 4.9,4.9 0 0 0 -4.53,2.63 v -8.69 a 0.43,0.43 0 0 0 -0.46,-0.46 h -3.3 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 h 3.3 a 0.43,0.43 0 0 0 0.46,-0.46 v -7.81 c 0,-2.11 1.16,-3.24 2.94,-3.24 1.78,0 2.78,1.13 2.78,3.24 v 7.77 a 0.44,0.44 0 0 0 0.46,0.46 h 3.28 a 0.46,0.46 0 0 0 0.49,-0.46 v -8.72 c 0,-3.92 -2.33,-6.18 -5.42,-6.18 z m 14.78,15.73 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 118.69,49 l -0.06,1.65 a 5.49,5.49 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.87,7.88 7.09,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.79,4 z m 17,9.3 a 0.56,0.56 0 0 0 0.55,-0.36 l 7.92,-19.92 c 0.13,-0.34 0,-0.52 -0.36,-0.52 h -3.37 a 0.56,0.56 0 0 0 -0.55,0.36 l -3.55,9.73 -3.58,-9.73 a 0.56,0.56 0 0 0 -0.55,-0.36 h -3.37 c -0.33,0 -0.49,0.18 -0.36,0.52 l 5.78,14.5 -2.14,5.26 c -0.16,0.34 0,0.52 0.36,0.52 z m 15.09,-5.44 c 3,0 5.6,-1.56 5.63,-4.59 0,-2.3 -1.47,-3.58 -3.36,-4.35 l -2.12,-0.82 c -0.82,-0.31 -1.43,-0.74 -1.43,-1.47 0,-0.73 0.45,-1.2 1.43,-1.2 a 4.3,4.3 0 0 1 2.73,1.26 c 0.27,0.21 0.49,0.24 0.7,0 l 1.35,-1.62 a 0.49,0.49 0 0 0 0,-0.65 6.44,6.44 0 0 0 -5,-2.29 c -2.78,0 -5.23,1.65 -5.23,4.53 0,2.08 1.34,3.33 3.27,4.1 l 1.9,0.76 c 1.1,0.46 1.56,0.83 1.56,1.56 0,0.73 -0.67,1.26 -1.68,1.26 a 5.7,5.7 0 0 1 -3.42,-1.47 0.47,0.47 0 0 0 -0.77,0.12 l -1.07,1.5 a 0.76,0.76 0 0 0 -0.06,0.89 7.16,7.16 0 0 0 5.62,2.48 z m 14,0 c 1,0 2.54,-0.19 2.54,-0.89 v -2.23 c 0,-0.31 -0.22,-0.46 -0.55,-0.43 -0.33,0.03 -0.68,0 -0.95,0 a 1.2,1.2 0 0 1 -1.32,-1.32 v -7.22 h 2.36 a 0.44,0.44 0 0 0 0.46,-0.46 V 49 a 0.44,0.44 0 0 0 -0.46,-0.46 h -2.36 V 45 a 0.43,0.43 0 0 0 -0.46,-0.46 H 156.6 A 0.43,0.43 0 0 0 156.14,45 v 3.52 h -1.9 a 0.43,0.43 0 0 0 -0.46,0.46 v 2.35 a 0.43,0.43 0 0 0 0.46,0.46 h 1.9 v 7.71 c 0.06,3.31 2.2,4.41 4.59,4.41 z m 11.45,0 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 177.12,49 l -0.06,1.65 a 5.48,5.48 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.97,7.88 7.15,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.73,4 z m 18.79,3.86 a 7.17,7.17 0 0 0 5.76,-2.7 0.44,0.44 0 0 0 -0.07,-0.64 l -1.83,-1.8 a 0.54,0.54 0 0 0 -0.8,0 3.85,3.85 0 0 1 -2.81,1.23 3.9,3.9 0 0 1 -4,-4 3.78,3.78 0 0 1 3.95,-3.93 3.85,3.85 0 0 1 2.94,1.25 0.56,0.56 0 0 0 0.8,0 l 1.83,-1.81 a 0.43,0.43 0 0 0 0.06,-0.64 7.19,7.19 0 0 0 -5.81,-2.69 7.63,7.63 0 0 0 -7.83,7.8 7.74,7.74 0 0 0 7.87,7.93 z m 12.5,-0.37 a 0.43,0.43 0 0 0 0.46,-0.46 V 59.9 l 1.87,-2.11 4,5.47 a 0.57,0.57 0 0 0 0.55,0.28 h 3.55 c 0.37,0 0.49,-0.24 0.27,-0.55 l -5.87,-8.11 5.08,-5.78 c 0.24,-0.31 0.15,-0.55 -0.25,-0.55 h -3.66 a 0.67,0.67 0 0 0 -0.55,0.24 l -4.93,5.81 V 42.12 a 0.43,0.43 0 0 0 -0.46,-0.46 H 201 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 z"
|
||||
id="path61" />
|
||||
<path
|
||||
class="cls-4"
|
||||
d="m 94.88,81.26 a 3.88,3.88 0 0 0 0,-7.71 2.7,2.7 0 0 0 -2.28,1 v -4 a 0.21,0.21 0 0 0 -0.22,-0.23 h -1.64 a 0.21,0.21 0 0 0 -0.22,0.23 v 10.3 a 0.22,0.22 0 0 0 0.24,0.23 h 1.47 a 0.21,0.21 0 0 0 0.22,-0.23 v -0.81 a 2.74,2.74 0 0 0 2.43,1.22 z m -0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.9,4.56 a 0.27,0.27 0 0 0 0.27,-0.18 L 106.51,74 c 0.06,-0.16 0,-0.25 -0.18,-0.25 h -1.65 a 0.27,0.27 0 0 0 -0.27,0.18 l -1.74,4.77 -1.76,-4.77 a 0.27,0.27 0 0 0 -0.27,-0.18 H 99 c -0.16,0 -0.24,0.09 -0.18,0.25 l 2.84,7.11 -1.07,2.58 c -0.07,0.17 0,0.26 0.18,0.26 z M 113.9,81.26 A 2.76,2.76 0 0 0 116.3,80 v 0.81 a 0.21,0.21 0 0 0 0.22,0.23 H 118 a 0.23,0.23 0 0 0 0.24,-0.23 V 70.58 a 0.22,0.22 0 0 0 -0.23,-0.23 h -1.63 a 0.22,0.22 0 0 0 -0.23,0.23 v 4 a 2.65,2.65 0 0 0 -2.26,-1 3.87,3.87 0 0 0 0,7.71 z m 0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 9.37,1.89 a 3.61,3.61 0 0 0 2.6,-0.93 c 0.14,-0.12 0.16,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.16,0.16 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.17,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.04,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 10.3,4.53 a 3.65,3.65 0 0 0 2.61,-0.93 c 0.13,-0.12 0.15,-0.23 0.07,-0.33 l -0.55,-0.77 a 0.18,0.18 0 0 0 -0.26,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.16,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.75,3.82 3.81,3.81 0 0 0 4,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 8.56,7.2 a 0.21,0.21 0 0 0 0.22,-0.23 v -3.49 a 2.76,2.76 0 0 0 2.22,1.05 3.88,3.88 0 0 0 0,-7.71 2.69,2.69 0 0 0 -2.4,1.21 v -0.81 a 0.21,0.21 0 0 0 -0.22,-0.22 h -1.48 a 0.21,0.21 0 0 0 -0.22,0.22 v 9.75 a 0.21,0.21 0 0 0 0.22,0.23 z m 2.07,-4.56 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.68,1.89 c 1.47,0 2.75,-0.77 2.76,-2.25 a 2.22,2.22 0 0 0 -1.65,-2.13 l -1,-0.41 c -0.41,-0.15 -0.71,-0.36 -0.71,-0.72 0,-0.36 0.23,-0.58 0.71,-0.58 a 2.12,2.12 0 0 1 1.33,0.61 c 0.14,0.11 0.24,0.12 0.35,0 l 0.66,-0.79 a 0.24,0.24 0 0 0 0,-0.32 3.16,3.16 0 0 0 -2.46,-1.12 2.32,2.32 0 0 0 -2.56,2.22 2.15,2.15 0 0 0 1.6,2 l 0.93,0.37 c 0.54,0.23 0.77,0.41 0.77,0.77 0,0.36 -0.33,0.61 -0.83,0.61 a 2.76,2.76 0 0 1 -1.69,-0.72 0.23,0.23 0 0 0 -0.38,0.06 l -0.52,0.74 a 0.35,0.35 0 0 0 0,0.43 3.5,3.5 0 0 0 2.67,1.23 z m 7.82,0 a 3.63,3.63 0 0 0 2.61,-0.93 c 0.14,-0.12 0.15,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.17,0.17 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.18,-1.44 h 4.47 c 0.65,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.52,-3.51 3.66,3.66 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.01,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.67,1.47 z m 9.48,4.53 c 0.5,0 1.25,-0.09 1.25,-0.44 v -1.09 c 0,-0.15 -0.11,-0.23 -0.27,-0.21 h -0.47 a 0.58,0.58 0 0 1 -0.64,-0.64 v -3.55 h 1.15 a 0.21,0.21 0 0 0 0.23,-0.22 V 74 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.15 V 72 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.63 A 0.21,0.21 0 0 0 161.39,72 v 1.73 h -0.93 a 0.21,0.21 0 0 0 -0.22,0.22 v 1.16 a 0.21,0.21 0 0 0 0.22,0.22 h 0.93 v 3.78 a 2,2 0 0 0 2.16,2.15 z"
|
||||
id="path63" />
|
||||
<path
|
||||
class="cls-5"
|
||||
d="m 94.88,81.26 a 3.88,3.88 0 0 0 0,-7.71 2.7,2.7 0 0 0 -2.28,1 v -4 a 0.21,0.21 0 0 0 -0.22,-0.23 h -1.64 a 0.21,0.21 0 0 0 -0.22,0.23 v 10.3 a 0.22,0.22 0 0 0 0.24,0.23 h 1.47 a 0.21,0.21 0 0 0 0.22,-0.23 v -0.81 a 2.74,2.74 0 0 0 2.43,1.22 z m -0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.9,4.56 a 0.27,0.27 0 0 0 0.27,-0.18 L 106.51,74 c 0.06,-0.16 0,-0.25 -0.18,-0.25 h -1.65 a 0.27,0.27 0 0 0 -0.27,0.18 l -1.74,4.77 -1.76,-4.77 a 0.27,0.27 0 0 0 -0.27,-0.18 H 99 c -0.16,0 -0.24,0.09 -0.18,0.25 l 2.84,7.11 -1.07,2.58 c -0.07,0.17 0,0.26 0.18,0.26 z M 113.9,81.26 A 2.76,2.76 0 0 0 116.3,80 v 0.81 a 0.21,0.21 0 0 0 0.22,0.23 H 118 a 0.23,0.23 0 0 0 0.24,-0.23 V 70.58 a 0.22,0.22 0 0 0 -0.23,-0.23 h -1.63 a 0.22,0.22 0 0 0 -0.23,0.23 v 4 a 2.65,2.65 0 0 0 -2.26,-1 3.87,3.87 0 0 0 0,7.71 z m 0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 9.37,1.89 a 3.61,3.61 0 0 0 2.6,-0.93 c 0.14,-0.12 0.16,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.16,0.16 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.17,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.04,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 10.3,4.53 a 3.65,3.65 0 0 0 2.61,-0.93 c 0.13,-0.12 0.15,-0.23 0.07,-0.33 l -0.55,-0.77 a 0.18,0.18 0 0 0 -0.26,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.16,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.75,3.82 3.81,3.81 0 0 0 4,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 8.56,7.2 a 0.21,0.21 0 0 0 0.22,-0.23 v -3.49 a 2.76,2.76 0 0 0 2.22,1.05 3.88,3.88 0 0 0 0,-7.71 2.69,2.69 0 0 0 -2.4,1.21 v -0.81 a 0.21,0.21 0 0 0 -0.22,-0.22 h -1.48 a 0.21,0.21 0 0 0 -0.22,0.22 v 9.75 a 0.21,0.21 0 0 0 0.22,0.23 z m 2.07,-4.56 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.68,1.89 c 1.47,0 2.75,-0.77 2.76,-2.25 a 2.22,2.22 0 0 0 -1.65,-2.13 l -1,-0.41 c -0.41,-0.15 -0.71,-0.36 -0.71,-0.72 0,-0.36 0.23,-0.58 0.71,-0.58 a 2.12,2.12 0 0 1 1.33,0.61 c 0.14,0.11 0.24,0.12 0.35,0 l 0.66,-0.79 a 0.24,0.24 0 0 0 0,-0.32 3.16,3.16 0 0 0 -2.46,-1.12 2.32,2.32 0 0 0 -2.56,2.22 2.15,2.15 0 0 0 1.6,2 l 0.93,0.37 c 0.54,0.23 0.77,0.41 0.77,0.77 0,0.36 -0.33,0.61 -0.83,0.61 a 2.76,2.76 0 0 1 -1.69,-0.72 0.23,0.23 0 0 0 -0.38,0.06 l -0.52,0.74 a 0.35,0.35 0 0 0 0,0.43 3.5,3.5 0 0 0 2.67,1.23 z m 7.82,0 a 3.63,3.63 0 0 0 2.61,-0.93 c 0.14,-0.12 0.15,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.17,0.17 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.18,-1.44 h 4.47 c 0.65,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.52,-3.51 3.66,3.66 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.01,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.67,1.47 z m 9.48,4.53 c 0.5,0 1.25,-0.09 1.25,-0.44 v -1.09 c 0,-0.15 -0.11,-0.23 -0.27,-0.21 h -0.47 a 0.58,0.58 0 0 1 -0.64,-0.64 v -3.55 h 1.15 a 0.21,0.21 0 0 0 0.23,-0.22 V 74 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.15 V 72 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.63 A 0.21,0.21 0 0 0 161.39,72 v 1.73 h -0.93 a 0.21,0.21 0 0 0 -0.22,0.22 v 1.16 a 0.21,0.21 0 0 0 0.22,0.22 h 0.93 v 3.78 a 2,2 0 0 0 2.16,2.15 z"
|
||||
id="path65" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="Smarter search with NLP + FastAPI"
|
||||
transform="translate(-3.3488372,-37.07907)"
|
||||
id="text25871"
|
||||
style="font-size:14px;line-height:1.25;white-space:pre;shape-inside:url(#rect25873);fill:#2b2f55">
|
||||
<path
|
||||
d="m 18.379625,123.61004 c 1.666,0 3.276,-0.91 3.276,-2.884 0,-1.652 -1.12,-2.31 -2.184,-2.814 l -1.008,-0.462 c -0.756,-0.35 -1.148,-0.574 -1.148,-1.148 0,-0.588 0.448,-0.938 1.162,-0.938 0.616,0 1.106,0.266 1.708,0.798 0.112,0.084 0.224,0.056 0.308,-0.056 l 0.798,-0.966 c 0.084,-0.112 0.084,-0.224 -0.014,-0.322 -0.77,-0.826 -1.708,-1.288 -2.87,-1.288 -1.666,0 -3.024,0.98 -3.024,2.758 0,1.456 0.924,2.198 1.96,2.674 l 1.19,0.56 c 0.686,0.308 1.176,0.546 1.176,1.204 0,0.7 -0.546,1.05 -1.372,1.05 -0.798,0 -1.358,-0.336 -1.974,-0.966 -0.112,-0.098 -0.224,-0.084 -0.322,0.028 l -0.812,0.938 c -0.098,0.112 -0.098,0.21 -0.028,0.308 0.574,0.784 1.652,1.526 3.178,1.526 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58197" />
|
||||
<path
|
||||
d="m 24.82925,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.598 c 0,-0.938 0.42,-1.442 1.148,-1.442 0.742,0 1.092,0.504 1.092,1.442 v 3.598 c 0,0.126 0.084,0.21 0.224,0.21 h 1.498 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.598 c 0,-0.938 0.42,-1.442 1.148,-1.442 0.756,0 1.092,0.504 1.092,1.442 v 3.598 c 0,0.126 0.084,0.21 0.21,0.21 h 1.512 c 0.126,0 0.21,-0.084 0.21,-0.21 v -4.018 c 0,-1.792 -0.896,-2.8 -2.366,-2.8 -1.064,0 -1.736,0.518 -2.114,1.218 -0.378,-0.798 -1.064,-1.218 -1.946,-1.218 -0.938,0 -1.554,0.476 -1.932,1.12 l -0.042,-0.728 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58199" />
|
||||
<path
|
||||
d="m 37.941125,123.61004 c 1.022,0 1.764,-0.434 2.24,-1.134 l 0.028,0.756 c 0,0.126 0.084,0.21 0.21,0.21 h 1.358 c 0.126,0 0.224,-0.084 0.224,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.372 c -0.126,0 -0.21,0.084 -0.21,0.21 l -0.028,0.756 c -0.462,-0.714 -1.204,-1.134 -2.24,-1.134 -1.932,0 -3.262,1.568 -3.262,3.598 0,2.044 1.33,3.598 3.262,3.598 z m 0.406,-1.764 c -1.022,0 -1.778,-0.728 -1.778,-1.834 0,-1.092 0.756,-1.834 1.778,-1.834 1.022,0 1.736,0.728 1.736,1.834 0,1.106 -0.714,1.834 -1.736,1.834 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58201" />
|
||||
<path
|
||||
d="m 45.39175,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.43 c 0,-0.98 0.504,-1.638 1.456,-1.638 0.196,0 0.35,0.028 0.504,0.07 0.182,0.042 0.28,0 0.28,-0.168 v -1.204 c 0,-0.126 -0.028,-0.21 -0.126,-0.28 -0.112,-0.084 -0.308,-0.168 -0.63,-0.168 -0.812,0 -1.246,0.546 -1.484,1.274 l -0.056,-0.882 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58203" />
|
||||
<path
|
||||
d="m 51.950531,123.61004 c 0.462,0 1.162,-0.084 1.162,-0.406 v -1.022 c 0,-0.14 -0.098,-0.21 -0.252,-0.196 -0.168,0.014 -0.308,0.014 -0.434,0.014 -0.364,0 -0.602,-0.196 -0.602,-0.602 v -3.318 h 1.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.078 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.078 v -1.61 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.61 h -0.868 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.078 c 0,0.126 0.084,0.21 0.21,0.21 h 0.868 v 3.528 c 0,1.498 0.98,2.002 2.072,2.002 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58205" />
|
||||
<path
|
||||
d="m 57.667781,123.61004 c 0.966,0 1.848,-0.28 2.436,-0.868 0.126,-0.112 0.14,-0.21 0.07,-0.308 l -0.518,-0.714 c -0.07,-0.098 -0.14,-0.112 -0.238,-0.056 -0.56,0.322 -1.064,0.42 -1.596,0.42 -1.106,0 -1.82,-0.476 -2.03,-1.344 h 4.172 c 0.602,0 0.756,-0.378 0.756,-1.05 0,-1.736 -1.204,-3.276 -3.29,-3.276 -2.114,0 -3.528,1.554 -3.528,3.57 0,2.072 1.512,3.626 3.766,3.626 z m -1.904,-4.228 c 0.168,-0.91 0.826,-1.372 1.68,-1.372 0.826,0 1.442,0.462 1.554,1.372 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58207" />
|
||||
<path
|
||||
d="m 63.739406,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.43 c 0,-0.98 0.504,-1.638 1.456,-1.638 0.196,0 0.35,0.028 0.504,0.07 0.182,0.042 0.28,0 0.28,-0.168 v -1.204 c 0,-0.126 -0.028,-0.21 -0.126,-0.28 -0.112,-0.084 -0.308,-0.168 -0.63,-0.168 -0.812,0 -1.246,0.546 -1.484,1.274 l -0.056,-0.882 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58209" />
|
||||
<path
|
||||
d="m 72.367125,123.61004 c 1.372,0 2.562,-0.714 2.576,-2.1 0.014,-1.05 -0.672,-1.638 -1.54,-1.988 l -0.966,-0.378 c -0.378,-0.14 -0.658,-0.336 -0.658,-0.672 0,-0.294 0.21,-0.546 0.658,-0.546 0.406,0 0.798,0.196 1.246,0.574 0.126,0.098 0.224,0.112 0.322,0 l 0.616,-0.742 c 0.07,-0.084 0.098,-0.196 0.014,-0.294 -0.574,-0.686 -1.4,-1.05 -2.296,-1.05 -1.274,0 -2.394,0.756 -2.394,2.072 0,0.952 0.616,1.526 1.498,1.876 l 0.868,0.35 c 0.504,0.21 0.714,0.378 0.714,0.714 0,0.392 -0.308,0.574 -0.77,0.574 -0.532,0 -1.008,-0.238 -1.582,-0.672 -0.112,-0.084 -0.238,-0.112 -0.35,0.056 l -0.49,0.686 c -0.098,0.154 -0.112,0.308 -0.028,0.406 0.546,0.644 1.414,1.134 2.562,1.134 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58211" />
|
||||
<path
|
||||
d="m 79.652156,123.61004 c 0.966,0 1.848,-0.28 2.436,-0.868 0.126,-0.112 0.14,-0.21 0.07,-0.308 l -0.518,-0.714 c -0.07,-0.098 -0.14,-0.112 -0.238,-0.056 -0.56,0.322 -1.064,0.42 -1.596,0.42 -1.106,0 -1.82,-0.476 -2.03,-1.344 h 4.172 c 0.602,0 0.756,-0.378 0.756,-1.05 0,-1.736 -1.204,-3.276 -3.29,-3.276 -2.114,0 -3.528,1.554 -3.528,3.57 0,2.072 1.512,3.626 3.766,3.626 z m -1.904,-4.228 c 0.168,-0.91 0.826,-1.372 1.68,-1.372 0.826,0 1.442,0.462 1.554,1.372 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58213" />
|
||||
<path
|
||||
d="m 86.913781,123.61004 c 1.022,0 1.764,-0.434 2.24,-1.134 l 0.028,0.756 c 0,0.126 0.084,0.21 0.21,0.21 h 1.358 c 0.126,0 0.224,-0.084 0.224,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.372 c -0.126,0 -0.21,0.084 -0.21,0.21 l -0.028,0.756 c -0.462,-0.714 -1.204,-1.134 -2.24,-1.134 -1.932,0 -3.262,1.568 -3.262,3.598 0,2.044 1.33,3.598 3.262,3.598 z m 0.406,-1.764 c -1.022,0 -1.778,-0.728 -1.778,-1.834 0,-1.092 0.756,-1.834 1.778,-1.834 1.022,0 1.736,0.728 1.736,1.834 0,1.106 -0.714,1.834 -1.736,1.834 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58215" />
|
||||
<path
|
||||
d="m 94.364406,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.43 c 0,-0.98 0.504,-1.638 1.456,-1.638 0.196,0 0.35,0.028 0.504,0.07 0.182,0.042 0.28,0 0.28,-0.168 v -1.204 c 0,-0.126 -0.028,-0.21 -0.126,-0.28 -0.112,-0.084 -0.308,-0.168 -0.63,-0.168 -0.812,0 -1.246,0.546 -1.484,1.274 l -0.056,-0.882 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58217" />
|
||||
<path
|
||||
d="m 101.14106,123.61004 c 1.106,0 2.044,-0.476 2.632,-1.232 0.084,-0.098 0.056,-0.21 -0.028,-0.294 l -0.84,-0.826 c -0.112,-0.112 -0.266,-0.098 -0.364,0 -0.378,0.378 -0.784,0.574 -1.316,0.574 -1.134,0 -1.847998,-0.84 -1.847998,-1.848 0,-1.008 0.713998,-1.792 1.805998,-1.792 0.56,0 0.966,0.196 1.344,0.574 0.098,0.098 0.252,0.112 0.364,0 l 0.84,-0.826 c 0.084,-0.084 0.112,-0.196 0.028,-0.294 -0.588,-0.756 -1.526,-1.232 -2.66,-1.232 -2.057998,0 -3.583998,1.54 -3.583998,3.57 0,2.058 1.540001,3.626 3.625998,3.626 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58219" />
|
||||
<path
|
||||
d="m 109.1425,116.41404 c -1.022,0 -1.68,0.504 -2.072,1.204 v -3.976 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.512 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.59 c 0,0.126 0.084,0.21 0.21,0.21 h 1.512 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.556 c 0,-0.966 0.532,-1.484 1.344,-1.484 0.826,0 1.274,0.518 1.274,1.484 v 3.556 c 0,0.126 0.084,0.21 0.21,0.21 h 1.498 c 0.126,0 0.224,-0.084 0.224,-0.21 v -3.99 c 0,-1.792 -1.05,-2.828 -2.478,-2.828 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58221" />
|
||||
<path
|
||||
d="m 119.54997,123.44204 c 0.112,0 0.21,-0.056 0.252,-0.168 l 1.526,-4.452 1.54,4.452 c 0.042,0.112 0.14,0.168 0.252,0.168 h 1.554 c 0.112,0 0.21,-0.056 0.252,-0.168 l 2.142,-6.454 c 0.056,-0.154 -0.014,-0.238 -0.168,-0.238 h -1.456 c -0.112,0 -0.224,0.056 -0.266,0.168 l -1.372,4.452 -1.484,-4.452 c -0.042,-0.112 -0.14,-0.168 -0.252,-0.168 h -1.47 c -0.112,0 -0.21,0.056 -0.252,0.168 l -1.498,4.452 -1.372,-4.452 c -0.042,-0.112 -0.14,-0.168 -0.252,-0.168 h -1.47 c -0.154,0 -0.224,0.084 -0.168,0.238 l 2.156,6.454 c 0.042,0.112 0.14,0.168 0.252,0.168 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58223" />
|
||||
<path
|
||||
d="m 129.32,115.36404 c 0.616,0 1.064,-0.476 1.064,-1.078 0,-0.588 -0.448,-1.064 -1.064,-1.064 -0.616,0 -1.078,0.476 -1.078,1.064 0,0.602 0.462,1.078 1.078,1.078 z m 0.756,8.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58225" />
|
||||
<path
|
||||
d="m 134.556,123.61004 c 0.462,0 1.162,-0.084 1.162,-0.406 v -1.022 c 0,-0.14 -0.098,-0.21 -0.252,-0.196 -0.168,0.014 -0.308,0.014 -0.434,0.014 -0.364,0 -0.602,-0.196 -0.602,-0.602 v -3.318 h 1.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.078 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.078 v -1.61 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.61 h -0.868 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.078 c 0,0.126 0.084,0.21 0.21,0.21 h 0.868 v 3.528 c 0,1.498 0.98,2.002 2.072,2.002 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58227" />
|
||||
<path
|
||||
d="m 140.94328,116.41404 c -1.022,0 -1.68,0.504 -2.072,1.204 v -3.976 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.512 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.59 c 0,0.126 0.084,0.21 0.21,0.21 h 1.512 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.556 c 0,-0.966 0.532,-1.484 1.344,-1.484 0.826,0 1.274,0.518 1.274,1.484 v 3.556 c 0,0.126 0.084,0.21 0.21,0.21 h 1.498 c 0.126,0 0.224,-0.084 0.224,-0.21 v -3.99 c 0,-1.792 -1.05,-2.828 -2.478,-2.828 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58229" />
|
||||
<path
|
||||
d="m 153.96875,113.64204 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.384 l -3.5,-6.454 c -0.056,-0.098 -0.14,-0.14 -0.252,-0.14 h -1.862 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 h 1.582 c 0.126,0 0.21,-0.084 0.21,-0.21 v -6.384 l 3.5,6.454 c 0.056,0.098 0.14,0.14 0.252,0.14 h 1.862 c 0.126,0 0.21,-0.084 0.21,-0.21 v -9.38 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58231" />
|
||||
<path
|
||||
d="m 159.69847,121.66404 v -7.812 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.582 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 h 4.788 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.358 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58233" />
|
||||
<path
|
||||
d="m 166.21503,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.22 h 1.162 c 2.324,0 3.444,-1.302 3.444,-3.192 0,-1.876 -1.12,-3.178 -3.444,-3.178 h -2.954 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z m 0.21,-8.022 h 1.092 c 0.854,0 1.596,0.294 1.596,1.4 0,1.12 -0.742,1.414 -1.596,1.414 h -1.092 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58235" />
|
||||
<path
|
||||
d="m 178.2045,121.93004 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.554 h 1.554 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.008 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.554 v -1.554 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.008 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.554 h -1.554 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.008 c 0,0.126 0.084,0.21 0.21,0.21 h 1.554 v 1.554 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58237" />
|
||||
<path
|
||||
d="m 186.58612,123.44204 c 0.12601,0 0.21,-0.084 0.21,-0.21 v -3.934 h 2.94 c 0.126,0 0.224,-0.084 0.224,-0.21 v -1.358 c 0,-0.126 -0.098,-0.21 -0.224,-0.21 h -2.94 v -2.1 h 3.122 c 0.126,0 0.224,-0.084 0.224,-0.21 v -1.358 c 0,-0.126 -0.098,-0.21 -0.224,-0.21 h -4.914 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58239" />
|
||||
<path
|
||||
d="m 194.238,123.61004 c 1.022,0 1.764,-0.434 2.24,-1.134 l 0.028,0.756 c 0,0.126 0.084,0.21 0.21,0.21 h 1.358 c 0.126,0 0.224,-0.084 0.224,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.372 c -0.126,0 -0.21,0.084 -0.21,0.21 l -0.028,0.756 c -0.462,-0.714 -1.204,-1.134 -2.24,-1.134 -1.932,0 -3.262,1.568 -3.262,3.598 0,2.044 1.33,3.598 3.262,3.598 z m 0.406,-1.764 c -1.022,0 -1.778,-0.728 -1.778,-1.834 0,-1.092 0.756,-1.834 1.778,-1.834 1.022,0 1.736,0.728 1.736,1.834 0,1.106 -0.714,1.834 -1.736,1.834 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58241" />
|
||||
<path
|
||||
d="m 202.30462,123.61004 c 1.372,0 2.562,-0.714 2.576,-2.1 0.014,-1.05 -0.672,-1.638 -1.54,-1.988 l -0.966,-0.378 c -0.378,-0.14 -0.658,-0.336 -0.658,-0.672 0,-0.294 0.21,-0.546 0.658,-0.546 0.406,0 0.798,0.196 1.246,0.574 0.126,0.098 0.224,0.112 0.322,0 l 0.616,-0.742 c 0.07,-0.084 0.098,-0.196 0.014,-0.294 -0.574,-0.686 -1.4,-1.05 -2.296,-1.05 -1.274,0 -2.394,0.756 -2.394,2.072 0,0.952 0.616,1.526 1.498,1.876 l 0.868,0.35 c 0.504,0.21 0.714,0.378 0.714,0.714 0,0.392 -0.308,0.574 -0.77,0.574 -0.532,0 -1.008,-0.238 -1.582,-0.672 -0.112,-0.084 -0.238,-0.112 -0.35,0.056 l -0.49,0.686 c -0.098,0.154 -0.112,0.308 -0.028,0.406 0.546,0.644 1.414,1.134 2.562,1.134 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58243" />
|
||||
<path
|
||||
d="m 208.71225,123.61004 c 0.462,0 1.162,-0.084 1.162,-0.406 v -1.022 c 0,-0.14 -0.098,-0.21 -0.252,-0.196 -0.168,0.014 -0.308,0.014 -0.434,0.014 -0.364,0 -0.602,-0.196 -0.602,-0.602 v -3.318 h 1.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.078 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.078 v -1.61 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.61 h -0.868 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.078 c 0,0.126 0.084,0.21 0.21,0.21 h 0.868 v 3.528 c 0,1.498 0.98,2.002 2.072,2.002 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58245" />
|
||||
<path
|
||||
d="m 216.26153,113.81004 c -0.042,-0.112 -0.154,-0.168 -0.266,-0.168 h -1.694 c -0.112,0 -0.224,0.056 -0.266,0.168 l -3.276,9.394 c -0.056,0.14 0.014,0.238 0.168,0.238 h 1.652 c 0.112,0 0.196,-0.042 0.238,-0.168 l 0.476,-1.442 h 3.696 l 0.476,1.442 c 0.042,0.126 0.126,0.168 0.238,0.168 h 1.652 c 0.154,0 0.238,-0.098 0.182,-0.238 z m -1.12,2.352 1.274,3.892 h -2.548 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58247" />
|
||||
<path
|
||||
d="m 222.70722,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.22 h 1.162 c 2.324,0 3.444,-1.302 3.444,-3.192 0,-1.876 -1.12,-3.178 -3.444,-3.178 h -2.954 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z m 0.21,-8.022 h 1.092 c 0.854,0 1.596,0.294 1.596,1.4 0,1.12 -0.742,1.414 -1.596,1.414 h -1.092 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58249" />
|
||||
<path
|
||||
d="m 230.82831,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -9.38 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.582 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58251" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/en/docs/img/tutorial/extending-openapi/image02.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
docs/en/docs/img/tutorial/extending-openapi/image03.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
docs/en/docs/img/tutorial/extending-openapi/image04.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -5,8 +5,8 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
|
||||
@@ -14,6 +14,9 @@
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -131,7 +134,7 @@ You will also need an ASGI server, for production such as <a href="https://www.u
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install uvicorn[standard]
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
@@ -317,7 +320,7 @@ And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" targe
|
||||
|
||||
### Recap
|
||||
|
||||
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
|
||||
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
|
||||
|
||||
You do that with standard modern Python types.
|
||||
|
||||
@@ -374,7 +377,7 @@ Coming back to the previous code example, **FastAPI** will:
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* 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 it has a required attribute `name` that should be a `str`.
|
||||
* 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.
|
||||
@@ -419,9 +422,9 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
|
||||
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
@@ -443,12 +446,10 @@ Used by Pydantic:
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Required if you want to use `FileResponse` or `StaticFiles`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
|
||||
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
|
||||
|
||||
Used by FastAPI / Starlette:
|
||||
@@ -456,7 +457,7 @@ Used by FastAPI / Starlette:
|
||||
* <a href="https://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]`.
|
||||
You can install all of these with `pip install "fastapi[all]"`.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -148,37 +148,62 @@ You can use, for example:
|
||||
|
||||
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
|
||||
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`.
|
||||
These types that have internal types are called "**generic**" types. And it's possible to declare them, even with their internal types.
|
||||
|
||||
It exists specifically to support these type hints.
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`. It exists specifically to support these type hints.
|
||||
|
||||
#### `List`
|
||||
#### Newer versions of Python
|
||||
|
||||
The syntax using `typing` is **compatible** with all versions, from Python 3.6 to the latest ones, including Python 3.9, Python 3.10, etc.
|
||||
|
||||
As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations.
|
||||
|
||||
If you can chose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity. See some examples below.
|
||||
|
||||
#### List
|
||||
|
||||
For example, let's define a variable to be a `list` of `str`.
|
||||
|
||||
From `typing`, import `List` (with a capital `L`):
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
From `typing`, import `List` (with a capital `L`):
|
||||
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
``` Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
As the type, put the `List`.
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
As the type, put the `List` that you imported from `typing`.
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
|
||||
!!! tip
|
||||
```Python hl_lines="4"
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
|
||||
As the type, put `list`.
|
||||
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Those internal types in the square brackets are called "type parameters".
|
||||
|
||||
In this case, `str` is the type parameter passed to `List`.
|
||||
In this case, `str` is the type parameter passed to `List` (or `list` in Python 3.9 and above).
|
||||
|
||||
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
|
||||
|
||||
!!! tip
|
||||
If you use Python 3.9 or above, you don't have to import `List` from `typing`, you can use the same regular `list` type instead.
|
||||
|
||||
By doing that, your editor can provide support even while processing items from the list:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
@@ -189,20 +214,28 @@ Notice that the variable `item` is one of the elements in the list `items`.
|
||||
|
||||
And still, the editor knows it is a `str`, and provides support for that.
|
||||
|
||||
#### `Tuple` and `Set`
|
||||
#### Tuple and Set
|
||||
|
||||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
* The variable `items_t` is a `tuple` with 3 items, an `int`, another `int`, and a `str`.
|
||||
* The variable `items_s` is a `set`, and each of its items is of type `bytes`.
|
||||
|
||||
#### `Dict`
|
||||
#### Dict
|
||||
|
||||
To define a `dict`, you pass 2 type parameters, separated by commas.
|
||||
|
||||
@@ -210,9 +243,17 @@ The first type parameter is for the keys of the `dict`.
|
||||
|
||||
The second type parameter is for the values of the `dict`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
@@ -220,9 +261,33 @@ This means:
|
||||
* The keys of this `dict` are of type `str` (let's say, the name of each item).
|
||||
* The values of this `dict` are of type `float` (let's say, the price of each item).
|
||||
|
||||
#### `Optional`
|
||||
#### Union
|
||||
|
||||
You can also use `Optional` to declare that a variable has a type, like `str`, but that it is "optional", which means that it could also be `None`:
|
||||
You can declare that a variable can be any of **several types**, for example, an `int` or a `str`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept.
|
||||
|
||||
In Python 3.10 there's also an **alternative syntax** were you can put the possible types separated by a <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr>.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008b.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
In both cases this means that `item` could be an `int` or a `str`.
|
||||
|
||||
#### Possibly `None`
|
||||
|
||||
You can declare that a value could have a type, like `str`, but that it could also be `None`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
@@ -230,18 +295,73 @@ You can also use `Optional` to declare that a variable has a type, like `str`, b
|
||||
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detecting errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent.
|
||||
|
||||
This also means that in Python 3.10, you can use `Something | None`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6 and above - alternative"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009b.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
#### Generic types
|
||||
|
||||
These types that take type parameters in square brackets, like:
|
||||
These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example:
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
are called **Generic types** or **Generics**.
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
You can use the same builtin types as generics (with square brakets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And the same as with Python 3.6, from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
You can use the same builtin types as generics (with square brakets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And the same as with Python 3.6, from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional` (the same as with Python 3.6)
|
||||
* ...and others.
|
||||
|
||||
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr> to declare unions of types.
|
||||
|
||||
### Classes as types
|
||||
|
||||
@@ -275,11 +395,25 @@ Then you create an instance of that class with some values and it will validate
|
||||
|
||||
And you get all the editor support with that resulting object.
|
||||
|
||||
Taken from the official Pydantic docs:
|
||||
An example from the official Pydantic docs:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
To learn more about <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic, check its docs</a>.
|
||||
|
||||
@@ -3,6 +3,185 @@
|
||||
## Latest Changes
|
||||
|
||||
|
||||
## 0.72.0
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Enable configuring Swagger UI parameters. Original PR [#2568](https://github.com/tiangolo/fastapi/pull/2568) by [@jmriebold](https://github.com/jmriebold). Here are the new docs: [Configuring Swagger UI](https://fastapi.tiangolo.com/advanced/extending-openapi/#configuring-swagger-ui).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Update Python Types docs, add missing 3.6 / 3.9 example. PR [#4434](https://github.com/tiangolo/fastapi/pull/4434) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Update Chinese translation for `docs/help-fastapi.md`. PR [#3847](https://github.com/tiangolo/fastapi/pull/3847) by [@jaystone776](https://github.com/jaystone776).
|
||||
* 🌐 Fix Korean translation for `docs/ko/docs/index.md`. PR [#4195](https://github.com/tiangolo/fastapi/pull/4195) by [@kty4119](https://github.com/kty4119).
|
||||
* 🌐 Add Polish translation for `docs/pl/docs/index.md`. PR [#4245](https://github.com/tiangolo/fastapi/pull/4245) by [@MicroPanda123](https://github.com/MicroPanda123).
|
||||
* 🌐 Add Chinese translation for `docs\tutorial\path-operation-configuration.md`. PR [#3312](https://github.com/tiangolo/fastapi/pull/3312) by [@jaystone776](https://github.com/jaystone776).
|
||||
|
||||
### Internal
|
||||
|
||||
* 🔧 Enable MkDocs Material Insiders' `content.tabs.link`. PR [#4399](https://github.com/tiangolo/fastapi/pull/4399) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.71.0
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add docs and tests for Python 3.9 and Python 3.10. PR [#3712](https://github.com/tiangolo/fastapi/pull/3712) by [@tiangolo](https://github.com/tiangolo).
|
||||
* You can start with [Python Types Intro](https://fastapi.tiangolo.com/python-types/), it explains what changes between different Python versions, in Python 3.9 and in Python 3.10.
|
||||
* All the FastAPI docs are updated. Each code example in the docs that could use different syntax in Python 3.9 or Python 3.10 now has all the alternatives in tabs.
|
||||
* ⬆️ Upgrade Starlette to 0.17.1. PR [#4145](https://github.com/tiangolo/fastapi/pull/4145) by [@simondale00](https://github.com/simondale00).
|
||||
|
||||
### Internal
|
||||
|
||||
* 👥 Update FastAPI People. PR [#4354](https://github.com/tiangolo/fastapi/pull/4354) by [@github-actions[bot]](https://github.com/apps/github-actions).
|
||||
* 🔧 Add FastAPI Trove Classifier for PyPI as now there's one 🤷😁. PR [#4386](https://github.com/tiangolo/fastapi/pull/4386) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Upgrade MkDocs Material and configs. PR [#4385](https://github.com/tiangolo/fastapi/pull/4385) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.70.1
|
||||
|
||||
There's nothing interesting in this particular FastAPI release. It is mainly to enable/unblock the release of the next version of Pydantic that comes packed with features and improvements. 🤩
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix JSON Schema for dataclasses, supporting the fixes in Pydantic 1.9. PR [#4272](https://github.com/tiangolo/fastapi/pull/4272) by [@PrettyWood](https://github.com/PrettyWood).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Korean translation for `docs/tutorial/request-forms-and-files.md`. PR [#3744](https://github.com/tiangolo/fastapi/pull/3744) by [@NinaHwang](https://github.com/NinaHwang).
|
||||
* 🌐 Add Korean translation for `docs/tutorial/request-files.md`. PR [#3743](https://github.com/tiangolo/fastapi/pull/3743) by [@NinaHwang](https://github.com/NinaHwang).
|
||||
* 🌐 Add portuguese translation for `docs/tutorial/query-params-str-validations.md`. PR [#3965](https://github.com/tiangolo/fastapi/pull/3965) by [@leandrodesouzadev](https://github.com/leandrodesouzadev).
|
||||
* 🌐 Add Korean translation for `docs/tutorial/response-status-code.md`. PR [#3742](https://github.com/tiangolo/fastapi/pull/3742) by [@NinaHwang](https://github.com/NinaHwang).
|
||||
* 🌐 Add Korean translation for Tutorial - JSON Compatible Encoder. PR [#3152](https://github.com/tiangolo/fastapi/pull/3152) by [@NEONKID](https://github.com/NEONKID).
|
||||
* 🌐 Add Korean translation for Tutorial - Path Parameters and Numeric Validations. PR [#2432](https://github.com/tiangolo/fastapi/pull/2432) by [@hard-coders](https://github.com/hard-coders).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/deployment/versions.md`. PR [#4121](https://github.com/tiangolo/fastapi/pull/4121) by [@DevDae](https://github.com/DevDae).
|
||||
* 🌐 Fix Korean translation for `docs/ko/docs/tutorial/index.md`. PR [#4193](https://github.com/tiangolo/fastapi/pull/4193) by [@kimjaeyoonn](https://github.com/kimjaeyoonn).
|
||||
* 🔧 Add CryptAPI sponsor. PR [#4264](https://github.com/tiangolo/fastapi/pull/4264) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Update `docs/tutorial/dependencies/classes-as-dependencies`: Add type of query parameters in a description of `Classes as dependencies`. PR [#4015](https://github.com/tiangolo/fastapi/pull/4015) by [@0417taehyun](https://github.com/0417taehyun).
|
||||
* 🌐 Add French translation for Tutorial - First steps. PR [#3455](https://github.com/tiangolo/fastapi/pull/3455) by [@Smlep](https://github.com/Smlep).
|
||||
* 🌐 Add French translation for `docs/tutorial/path-params.md`. PR [#3548](https://github.com/tiangolo/fastapi/pull/3548) by [@Smlep](https://github.com/Smlep).
|
||||
* 🌐 Add French translation for `docs/tutorial/query-params.md`. PR [#3556](https://github.com/tiangolo/fastapi/pull/3556) by [@Smlep](https://github.com/Smlep).
|
||||
* 🌐 Add Turkish translation for `docs/python-types.md`. PR [#3926](https://github.com/tiangolo/fastapi/pull/3926) by [@BilalAlpaslan](https://github.com/BilalAlpaslan).
|
||||
|
||||
### Internal
|
||||
|
||||
* 👥 Update FastAPI People. PR [#4274](https://github.com/tiangolo/fastapi/pull/4274) by [@github-actions[bot]](https://github.com/apps/github-actions).
|
||||
|
||||
## 0.70.0
|
||||
|
||||
This release just upgrades Starlette to the latest version, `0.16.0`, which includes several bug fixes and some small breaking changes.
|
||||
|
||||
These last **three consecutive releases** are independent so that you can **migrate gradually**:
|
||||
|
||||
* First to FastAPI `0.68.2`, with no breaking changes, but upgrading all the sub-dependencies.
|
||||
* Next to FastAPI `0.69.0`, which upgrades Starlette to `0.15.0`, with AnyIO support, and a higher chance of having breaking changes in your code.
|
||||
* Finally to FastAPI `0.70.0`, just upgrading Starlette to the latest version `0.16.0` with additional bug fixes.
|
||||
|
||||
This way, in case there was a breaking change for your code in one of the releases, you can still benefit from the previous upgrades. ✨
|
||||
|
||||
### Breaking Changes - Upgrade
|
||||
|
||||
* ⬆️ Upgrade Starlette to 0.16.0. PR [#4016](https://github.com/tiangolo/fastapi/pull/4016) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
Also upgrades the ranges of optional dependencies:
|
||||
|
||||
* `"jinja2 >=2.11.2,<4.0.0"`
|
||||
* `"itsdangerous >=1.1.0,<3.0.0"`
|
||||
|
||||
## 0.69.0
|
||||
|
||||
### Breaking Changes - Upgrade
|
||||
|
||||
This release adds support for [Trio](https://trio.readthedocs.io/en/stable/). ✨
|
||||
|
||||
It upgrades the version of Starlette to `0.15.0`, now based on [AnyIO](https://anyio.readthedocs.io/en/stable/), and the internal async components in **FastAPI** are now based on AnyIO as well, making it compatible with both **asyncio** and **Trio**.
|
||||
|
||||
You can read the docs about running [FastAPI with Trio using Hypercorn](https://fastapi.tiangolo.com/deployment/manually/#hypercorn-with-trio).
|
||||
|
||||
This release also removes `graphene` as an optional dependency for GraphQL. If you need to work with GraphQL, the recommended library now is [Strawberry](https://strawberry.rocks/). You can read the new [FastAPI with GraphQL docs](https://fastapi.tiangolo.com/advanced/graphql/).
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add support for Trio via AnyIO, upgrading Starlette to `0.15.0`. PR [#3372](https://github.com/tiangolo/fastapi/pull/3372) by [@graingert](https://github.com/graingert).
|
||||
* ➖ Remove `graphene` as an optional dependency. PR [#4007](https://github.com/tiangolo/fastapi/pull/4007) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Add docs for using Trio with Hypercorn. PR [#4014](https://github.com/tiangolo/fastapi/pull/4014) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✏ Fix typos in Deployment Guide. PR [#3975](https://github.com/tiangolo/fastapi/pull/3975) by [@ghandic](https://github.com/ghandic).
|
||||
* 📝 Update docs with pip install calls when using extras with brackets, use quotes for compatibility with Zsh. PR [#3131](https://github.com/tiangolo/fastapi/pull/3131) by [@tomwei7](https://github.com/tomwei7).
|
||||
* 📝 Add external link to article: Deploying ML Models as API Using FastAPI and Heroku. PR [#3904](https://github.com/tiangolo/fastapi/pull/3904) by [@kaustubhgupta](https://github.com/kaustubhgupta).
|
||||
* ✏ Fix typo in file paths in `docs/en/docs/contributing.md`. PR [#3752](https://github.com/tiangolo/fastapi/pull/3752) by [@NinaHwang](https://github.com/NinaHwang).
|
||||
* ✏ Fix a typo in `docs/en/docs/advanced/path-operation-advanced-configuration.md` and `docs/en/docs/release-notes.md`. PR [#3750](https://github.com/tiangolo/fastapi/pull/3750) by [@saintmalik](https://github.com/saintmalik).
|
||||
* ✏️ Add a missing comma in the security tutorial. PR [#3564](https://github.com/tiangolo/fastapi/pull/3564) by [@jalvaradosegura](https://github.com/jalvaradosegura).
|
||||
* ✏ Fix typo in `docs/en/docs/help-fastapi.md`. PR [#3760](https://github.com/tiangolo/fastapi/pull/3760) by [@jaystone776](https://github.com/jaystone776).
|
||||
* ✏ Fix typo about file path in `docs/en/docs/tutorial/bigger-applications.md`. PR [#3285](https://github.com/tiangolo/fastapi/pull/3285) by [@HolyDorus](https://github.com/HolyDorus).
|
||||
* ✏ Re-word to clarify test client in `docs/en/docs/tutorial/testing.md`. PR [#3382](https://github.com/tiangolo/fastapi/pull/3382) by [@Bharat123rox](https://github.com/Bharat123rox).
|
||||
* 📝 Fix incorrect highlighted code. PR [#3325](https://github.com/tiangolo/fastapi/pull/3325) by [@paxcodes](https://github.com/paxcodes).
|
||||
* 📝 Add external link to article: How-to deploy FastAPI app to Heroku. PR [#3241](https://github.com/tiangolo/fastapi/pull/3241) by [@Jarmos-san](https://github.com/Jarmos-san).
|
||||
* ✏ Fix typo (mistranslation) in `docs/en/docs/advanced/templates.md`. PR [#3211](https://github.com/tiangolo/fastapi/pull/3211) by [@oerpli](https://github.com/oerpli).
|
||||
* 📝 Remove note about (now supported) feature from Swagger UI in `docs/en/docs/tutorial/request-files.md`. PR [#2803](https://github.com/tiangolo/fastapi/pull/2803) by [@gsganden](https://github.com/gsganden).
|
||||
* ✏ Fix typo re-word in `docs/tutorial/handling-errors.md`. PR [#2700](https://github.com/tiangolo/fastapi/pull/2700) by [@graue70](https://github.com/graue70).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Initialize Azerbaijani translations. PR [#3941](https://github.com/tiangolo/fastapi/pull/3941) by [@madatbay](https://github.com/madatbay).
|
||||
* 🌐 Add Turkish translation for `docs/fastapi-people.md`. PR [#3848](https://github.com/tiangolo/fastapi/pull/3848) by [@BilalAlpaslan](https://github.com/BilalAlpaslan).
|
||||
|
||||
### Internal
|
||||
|
||||
* 📝 Add supported Python versions badge. PR [#2794](https://github.com/tiangolo/fastapi/pull/2794) by [@hramezani](https://github.com/hramezani).
|
||||
* ✏ Fix link in Japanese docs for `docs/ja/docs/deployment/docker.md`. PR [#3245](https://github.com/tiangolo/fastapi/pull/3245) by [@utamori](https://github.com/utamori).
|
||||
* 🔧 Correct DeprecationWarning config and comment in pytest settings. PR [#4008](https://github.com/tiangolo/fastapi/pull/4008) by [@graingert](https://github.com/graingert).
|
||||
* 🔧 Swap light/dark theme button icon. PR [#3246](https://github.com/tiangolo/fastapi/pull/3246) by [@eddsalkield](https://github.com/eddsalkield).
|
||||
* 🔧 Lint only in Python 3.7 and above. PR [#4006](https://github.com/tiangolo/fastapi/pull/4006) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Add GitHub Action notify-translations config for Azerbaijani. PR [#3995](https://github.com/tiangolo/fastapi/pull/3995) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.68.2
|
||||
|
||||
This release has **no breaking changes**. 🎉
|
||||
|
||||
It upgrades the version ranges of sub-dependencies to allow applications using FastAPI to easily upgrade them.
|
||||
|
||||
Soon there will be a new FastAPI release upgrading Starlette to take advantage of recent improvements, but as that has a higher chance of having breaking changes, it will be in a separate release.
|
||||
|
||||
### Features
|
||||
|
||||
* ⬆Increase supported version of aiofiles to suppress warnings. PR [#2899](https://github.com/tiangolo/fastapi/pull/2899) by [@SnkSynthesis](https://github.com/SnkSynthesis).
|
||||
* ➖ Do not require backports in Python >= 3.7. PR [#1880](https://github.com/tiangolo/fastapi/pull/1880) by [@FFY00](https://github.com/FFY00).
|
||||
* ⬆ Upgrade required Python version to >= 3.6.1, needed by typing.Deque, used by Pydantic. PR [#2733](https://github.com/tiangolo/fastapi/pull/2733) by [@hukkin](https://github.com/hukkin).
|
||||
* ⬆️ Bump Uvicorn max range to 0.15.0. PR [#3345](https://github.com/tiangolo/fastapi/pull/3345) by [@Kludex](https://github.com/Kludex).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Update GraphQL docs, recommend Strawberry. PR [#3981](https://github.com/tiangolo/fastapi/pull/3981) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Re-write and extend Deployment guide: Concepts, Uvicorn, Gunicorn, Docker, Containers, Kubernetes. PR [#3974](https://github.com/tiangolo/fastapi/pull/3974) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Upgrade HTTPS guide with more explanations and diagrams. PR [#3950](https://github.com/tiangolo/fastapi/pull/3950) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Turkish translation for `docs/features.md`. PR [#1950](https://github.com/tiangolo/fastapi/pull/1950) by [@ycd](https://github.com/ycd).
|
||||
* 🌐 Add Turkish translation for `docs/benchmarks.md`. PR [#2729](https://github.com/tiangolo/fastapi/pull/2729) by [@Telomeraz](https://github.com/Telomeraz).
|
||||
* 🌐 Add Turkish translation for `docs/index.md`. PR [#1908](https://github.com/tiangolo/fastapi/pull/1908) by [@ycd](https://github.com/ycd).
|
||||
* 🌐 Add French translation for `docs/tutorial/body.md`. PR [#3671](https://github.com/tiangolo/fastapi/pull/3671) by [@Smlep](https://github.com/Smlep).
|
||||
* 🌐 Add French translation for `deployment/docker.md`. PR [#3694](https://github.com/tiangolo/fastapi/pull/3694) by [@rjNemo](https://github.com/rjNemo).
|
||||
* 🌐 Add Portuguese translation for `docs/tutorial/path-params.md`. PR [#3664](https://github.com/tiangolo/fastapi/pull/3664) by [@FelipeSilva93](https://github.com/FelipeSilva93).
|
||||
* 🌐 Add Portuguese translation for `docs/deployment/https.md`. PR [#3754](https://github.com/tiangolo/fastapi/pull/3754) by [@lsglucas](https://github.com/lsglucas).
|
||||
* 🌐 Add German translation for `docs/features.md`. PR [#3699](https://github.com/tiangolo/fastapi/pull/3699) by [@mawassk](https://github.com/mawassk).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✨ Update GitHub Action: notify-translations, to avoid a race conditions. PR [#3989](https://github.com/tiangolo/fastapi/pull/3989) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆️ Upgrade development `autoflake`, supporting multi-line imports. PR [#3988](https://github.com/tiangolo/fastapi/pull/3988) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆️ Increase dependency ranges for tests and docs: pytest-cov, pytest-asyncio, black, httpx, sqlalchemy, databases, mkdocs-markdownextradata-plugin. PR [#3987](https://github.com/tiangolo/fastapi/pull/3987) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👥 Update FastAPI People. PR [#3986](https://github.com/tiangolo/fastapi/pull/3986) by [@github-actions[bot]](https://github.com/apps/github-actions).
|
||||
* 💚 Fix badges in README and main page. PR [#3979](https://github.com/tiangolo/fastapi/pull/3979) by [@ghandic](https://github.com/ghandic).
|
||||
* ⬆ Upgrade internal testing dependencies: mypy to version 0.910, add newly needed type packages. PR [#3350](https://github.com/tiangolo/fastapi/pull/3350) by [@ArcLightSlavik](https://github.com/ArcLightSlavik).
|
||||
* ✨ Add Deepset Sponsorship. PR [#3976](https://github.com/tiangolo/fastapi/pull/3976) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🎨 Tweak CSS styles for shell animations. PR [#3888](https://github.com/tiangolo/fastapi/pull/3888) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Add new Sponsor Calmcode.io. PR [#3777](https://github.com/tiangolo/fastapi/pull/3777) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.68.1
|
||||
|
||||
* ✨ Add support for `read_with_orm_mode`, to support [SQLModel](https://sqlmodel.tiangolo.com/) relationship attributes. PR [#3757](https://github.com/tiangolo/fastapi/pull/3757) by [@tiangolo](https://github.com/tiangolo).
|
||||
@@ -32,7 +211,7 @@
|
||||
### Features
|
||||
|
||||
* ✨ Add support for extensions and updates to the OpenAPI schema in each *path operation*. New docs: [FastAPI Path Operation Advanced Configuration - OpenAPI Extra](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#openapi-extra). Initial PR [#1922](https://github.com/tiangolo/fastapi/pull/1922) by [@edouardlp](https://github.com/edouardlp).
|
||||
* ✨ Add additonal OpenAPI metadata parameters to `FastAPI` class, shown on the automatic API docs UI. New docs: [Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/). Initial PR [#1812](https://github.com/tiangolo/fastapi/pull/1812) by [@dkreeft](https://github.com/dkreeft).
|
||||
* ✨ Add additional OpenAPI metadata parameters to `FastAPI` class, shown on the automatic API docs UI. New docs: [Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/). Initial PR [#1812](https://github.com/tiangolo/fastapi/pull/1812) by [@dkreeft](https://github.com/dkreeft).
|
||||
* ✨ Add `description` parameter to all the security scheme classes, e.g. `APIKeyQuery(name="key", description="A very cool API key")`. PR [#1757](https://github.com/tiangolo/fastapi/pull/1757) by [@hylkepostma](https://github.com/hylkepostma).
|
||||
* ✨ Update OpenAPI models, supporting recursive models and extensions. PR [#3628](https://github.com/tiangolo/fastapi/pull/3628) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✨ Import and re-export data structures from Starlette, used by Request properties, on `fastapi.datastructures`. Initial PR [#1872](https://github.com/tiangolo/fastapi/pull/1872) by [@jamescurtin](https://github.com/jamescurtin).
|
||||
|
||||
@@ -57,9 +57,17 @@ Using `BackgroundTasks` also works with the dependency injection system, you can
|
||||
|
||||
**FastAPI** knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards:
|
||||
|
||||
```Python hl_lines="13 15 22 25"
|
||||
{!../../../docs_src/background_tasks/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13 15 22 25"
|
||||
{!> ../../../docs_src/background_tasks/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="11 13 20 23"
|
||||
{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
In this example, the messages will be written to the `log.txt` file *after* the response is sent.
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ mean:
|
||||
|
||||
* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)...
|
||||
* go to the parent package (the directory `app/`)...
|
||||
* and in there, find the module `dependencies` (the file at `app/routers/dependencies.py`)...
|
||||
* and in there, find the module `dependencies` (the file at `app/dependencies.py`)...
|
||||
* and from it, import the function `get_token_header`.
|
||||
|
||||
That works correctly! 🎉
|
||||
@@ -252,7 +252,7 @@ that would mean:
|
||||
* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)...
|
||||
* go to the parent package (the directory `app/`)...
|
||||
* then go to the parent of that package (there's no parent package, `app` is the top level 😱)...
|
||||
* and in there, find the module `dependencies` (the file at `app/routers/dependencies.py`)...
|
||||
* and in there, find the module `dependencies` (the file at `app/dependencies.py`)...
|
||||
* and from it, import the function `get_token_header`.
|
||||
|
||||
That would refer to some package above `app/`, with its own file `__init__.py`, etc. But we don't have that. So, that would throw an error in our example. 🚨
|
||||
|
||||
@@ -6,9 +6,17 @@ The same way you can declare additional validation and metadata in *path operati
|
||||
|
||||
First, you have to import it:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!> ../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
|
||||
@@ -17,9 +25,17 @@ First, you have to import it:
|
||||
|
||||
You can then use `Field` with model attributes:
|
||||
|
||||
```Python hl_lines="11-14"
|
||||
{!../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="11-14"
|
||||
{!> ../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="9-12"
|
||||
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
|
||||
|
||||
|
||||
@@ -8,9 +8,17 @@ First, of course, you can mix `Path`, `Query` and request body parameter declara
|
||||
|
||||
And you can also declare body parameters as optional, by setting the default to `None`:
|
||||
|
||||
```Python hl_lines="19-21"
|
||||
{!../../../docs_src/body_multiple_params/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19-21"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-19"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value.
|
||||
@@ -30,9 +38,17 @@ In the previous example, the *path operations* would expect a JSON body with the
|
||||
|
||||
But you can also declare multiple body parameters, e.g. `item` and `user`:
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/body_multiple_params/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** will notice that there are more than one body parameters in the function (two parameters that are Pydantic models).
|
||||
|
||||
@@ -71,14 +87,20 @@ If you declare it as is, because it is a singular value, **FastAPI** will assume
|
||||
|
||||
But you can instruct **FastAPI** to treat it as another body key using `Body`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="23"
|
||||
{!../../../docs_src/body_multiple_params/tutorial003.py!}
|
||||
```
|
||||
```Python hl_lines="23"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** will expect a body like:
|
||||
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": {
|
||||
@@ -107,12 +129,26 @@ As, by default, singular values are interpreted as query parameters, you don't h
|
||||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
as in:
|
||||
Or in Python 3.10 and above:
|
||||
|
||||
```Python hl_lines="27"
|
||||
{!../../../docs_src/body_multiple_params/tutorial004.py!}
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="28"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="26"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
`Body` also has all the same extra validation and metadata parameters as `Query`,`Path` and others you will see later.
|
||||
|
||||
@@ -131,9 +167,17 @@ item: Item = Body(..., embed=True)
|
||||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!../../../docs_src/body_multiple_params/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
In this case **FastAPI** will expect a body like:
|
||||
|
||||
|
||||
@@ -6,9 +6,17 @@ With **FastAPI**, you can define, validate, document, and use arbitrarily deeply
|
||||
|
||||
You can define an attribute to be a subtype. For example, a Python `list`:
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!../../../docs_src/body_nested_models/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
This will make `tags` be a list of items. Although it doesn't declare the type of each of the items.
|
||||
|
||||
@@ -18,19 +26,29 @@ But Python has a specific way to declare lists with internal types, or "type par
|
||||
|
||||
### Import typing's `List`
|
||||
|
||||
First, import `List` from standard Python's `typing` module:
|
||||
In Python 3.9 and above you can use the standard `list` to declare these type annotations as we'll see below. 💡
|
||||
|
||||
But in Python versions before 3.9 (3.6 and above), you first need to import `List` from standard Python's `typing` module:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Declare a `List` with a type parameter
|
||||
### Declare a `list` with a type parameter
|
||||
|
||||
To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`:
|
||||
|
||||
* Import them from the `typing` module
|
||||
* If you are in a Python version lower than 3.9, import their equivalent version from the `typing` module
|
||||
* Pass the internal type(s) as "type parameters" using square brackets: `[` and `]`
|
||||
|
||||
In Python 3.9 it would be:
|
||||
|
||||
```Python
|
||||
my_list: list[str]
|
||||
```
|
||||
|
||||
In versions of Python before 3.9, it would be:
|
||||
|
||||
```Python
|
||||
from typing import List
|
||||
|
||||
@@ -43,9 +61,23 @@ Use that same standard syntax for model attributes with internal types.
|
||||
|
||||
So, in our example, we can make `tags` be specifically a "list of strings":
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Set types
|
||||
|
||||
@@ -53,11 +85,25 @@ But then we think about it, and realize that tags shouldn't repeat, they would p
|
||||
|
||||
And Python has a special data type for sets of unique items, the `set`.
|
||||
|
||||
Then we can import `Set` and declare `tags` as a `set` of `str`:
|
||||
Then we can declare `tags` as a set of strings:
|
||||
|
||||
```Python hl_lines="1 14"
|
||||
{!../../../docs_src/body_nested_models/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial003_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
|
||||
|
||||
@@ -79,17 +125,45 @@ All that, arbitrarily nested.
|
||||
|
||||
For example, we can define an `Image` model:
|
||||
|
||||
```Python hl_lines="9-11"
|
||||
{!../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9-11"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9-11"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7-9"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
### Use the submodel as a type
|
||||
|
||||
And then we can use it as the type of an attribute:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
This would mean that **FastAPI** would expect a body similar to:
|
||||
|
||||
@@ -122,9 +196,23 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
|
||||
|
||||
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`:
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
{!../../../docs_src/body_nested_models/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2 8"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
|
||||
|
||||
@@ -132,9 +220,23 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
|
||||
|
||||
You can also use Pydantic models as subtypes of `list`, `set`, etc:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!../../../docs_src/body_nested_models/tutorial006.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!}
|
||||
```
|
||||
|
||||
This will expect (convert, validate, document, etc) a JSON body like:
|
||||
|
||||
@@ -169,9 +271,23 @@ This will expect (convert, validate, document, etc) a JSON body like:
|
||||
|
||||
You can define arbitrarily deeply nested models:
|
||||
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!../../../docs_src/body_nested_models/tutorial007.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 12 18 21 25"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Notice how `Offer` has a list of `Item`s, which in turn have an optional list of `Image`s
|
||||
@@ -184,11 +300,25 @@ If the top level value of the JSON body you expect is a JSON `array` (a Python `
|
||||
images: List[Image]
|
||||
```
|
||||
|
||||
or in Python 3.9 and above:
|
||||
|
||||
```Python
|
||||
images: list[Image]
|
||||
```
|
||||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!../../../docs_src/body_nested_models/tutorial008.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial008.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
## Editor support everywhere
|
||||
|
||||
@@ -218,9 +348,17 @@ That's what we are going to see here.
|
||||
|
||||
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/body_nested_models/tutorial009.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Have in mind that JSON only supports `str` as keys.
|
||||
|
||||
@@ -6,9 +6,23 @@ To update an item you can use the <a href="https://developer.mozilla.org/en-US/d
|
||||
|
||||
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
|
||||
|
||||
```Python hl_lines="30-35"
|
||||
{!../../../docs_src/body_updates/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="30-35"
|
||||
{!> ../../../docs_src/body_updates/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="30-35"
|
||||
{!> ../../../docs_src/body_updates/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="28-33"
|
||||
{!> ../../../docs_src/body_updates/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
`PUT` is used to receive data that should replace the existing data.
|
||||
|
||||
@@ -53,9 +67,23 @@ That would generate a `dict` with only the data that was set when creating the `
|
||||
|
||||
Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!> ../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="32"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
### Using Pydantic's `update` parameter
|
||||
|
||||
@@ -63,9 +91,23 @@ Now, you can create a copy of the existing model using `.copy()`, and pass the `
|
||||
|
||||
Like `stored_item_model.copy(update=update_data)`:
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!> ../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="33"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
### Partial updates recap
|
||||
|
||||
@@ -82,9 +124,23 @@ In summary, to apply partial updates you would:
|
||||
* Save the data to your DB.
|
||||
* Return the updated model.
|
||||
|
||||
```Python hl_lines="30-37"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="30-37"
|
||||
{!> ../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="30-37"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="28-35"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can actually use this same technique with an HTTP `PUT` operation.
|
||||
|
||||
@@ -19,9 +19,17 @@ To declare a **request** body, you use <a href="https://pydantic-docs.helpmanual
|
||||
|
||||
First, you need to import `BaseModel` from `pydantic`:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!> ../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!> ../../../docs_src/body/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Create your data model
|
||||
|
||||
@@ -29,9 +37,17 @@ Then you declare your data model as a class that inherits from `BaseModel`.
|
||||
|
||||
Use standard Python types for all the attributes:
|
||||
|
||||
```Python hl_lines="7-11"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="7-11"
|
||||
{!> ../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="5-9"
|
||||
{!> ../../../docs_src/body/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional.
|
||||
|
||||
@@ -59,9 +75,17 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
|
||||
|
||||
To add it to your *path operation*, declare it the same way you declared path and query parameters:
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!> ../../../docs_src/body/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
...and declare its type as the model you created, `Item`.
|
||||
|
||||
@@ -125,9 +149,17 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
|
||||
|
||||
Inside of the function, you can access all the attributes of the model object directly:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/body/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Request body + path parameters
|
||||
|
||||
@@ -135,9 +167,17 @@ You can declare path parameters and request body at the same time.
|
||||
|
||||
**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!> ../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15-16"
|
||||
{!> ../../../docs_src/body/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
## Request body + path + query parameters
|
||||
|
||||
@@ -145,9 +185,17 @@ You can also declare **body**, **path** and **query** parameters, all at the sam
|
||||
|
||||
**FastAPI** will recognize each of them and take the data from the correct place.
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!> ../../../docs_src/body/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
The function parameters will be recognized as follows:
|
||||
|
||||
|
||||
@@ -6,9 +6,17 @@ You can define Cookie parameters the same way you define `Query` and `Path` para
|
||||
|
||||
First import `Cookie`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!> ../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Declare `Cookie` parameters
|
||||
|
||||
@@ -16,9 +24,17 @@ Then declare the cookie parameters using the same structure as with `Path` and `
|
||||
|
||||
The first value is the default value, you can pass all the extra validation or annotation parameters:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
`Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class.
|
||||
|
||||
@@ -6,9 +6,17 @@ Before diving deeper into the **Dependency Injection** system, let's upgrade the
|
||||
|
||||
In the previous example, we were returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
But then we get a `dict` in the parameter `commons` of the *path operation function*.
|
||||
|
||||
@@ -71,29 +79,53 @@ That also applies to callables with no parameters at all. The same as it would b
|
||||
|
||||
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`:
|
||||
|
||||
```Python hl_lines="11-15"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="11-15"
|
||||
{!> ../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="9-13"
|
||||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
Pay attention to the `__init__` method used to create the instance of the class:
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
...it has the same parameters as our previous `common_parameters`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
Those parameters are what **FastAPI** will use to "solve" the dependency.
|
||||
|
||||
In both cases, it will have:
|
||||
|
||||
* an optional `q` query parameter.
|
||||
* a `skip` query parameter, with a default of `0`.
|
||||
* a `limit` query parameter, with a default of `100`.
|
||||
* An optional `q` query parameter that is a `str`.
|
||||
* A `skip` query parameter that is an `int`, with a default of `0`.
|
||||
* A `limit` query parameter that is an `int`, with a default of `100`.
|
||||
|
||||
In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.
|
||||
|
||||
@@ -101,9 +133,17 @@ In both cases the data will be converted, validated, documented on the OpenAPI s
|
||||
|
||||
Now you can declare your dependency using this class.
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function.
|
||||
|
||||
@@ -143,9 +183,17 @@ commons = Depends(CommonQueryParams)
|
||||
|
||||
..as in:
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/dependencies/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
But declaring the type is encouraged as that way your editor will know what will be passed as the parameter `commons`, and then it can help you with code completion, type checks, etc:
|
||||
|
||||
@@ -179,9 +227,17 @@ You declare the dependency as the type of the parameter, and you use `Depends()`
|
||||
|
||||
The same example would then look like:
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/dependencies/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
...and **FastAPI** will know what to do.
|
||||
|
||||
|
||||
@@ -7,15 +7,6 @@ To do this, use `yield` instead of `return`, and write the extra steps after.
|
||||
!!! tip
|
||||
Make sure to use `yield` one single time.
|
||||
|
||||
!!! info
|
||||
For this to work, you need to use **Python 3.7** or above, or in **Python 3.6**, install the "backports":
|
||||
|
||||
```
|
||||
pip install async-exit-stack async-generator
|
||||
```
|
||||
|
||||
This installs <a href="https://github.com/sorcio/async_exit_stack" class="external-link" target="_blank">async-exit-stack</a> and <a href="https://github.com/python-trio/async_generator" class="external-link" target="_blank">async-generator</a>.
|
||||
|
||||
!!! note "Technical Details"
|
||||
Any function that is valid to use with:
|
||||
|
||||
|
||||
@@ -31,9 +31,17 @@ Let's first focus on the dependency.
|
||||
|
||||
It is just a function that can take all the same parameters that a *path operation function* can take:
|
||||
|
||||
```Python hl_lines="8-9"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8-9"
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6-7"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
That's it.
|
||||
|
||||
@@ -55,17 +63,33 @@ And then it just returns a `dict` containing those values.
|
||||
|
||||
### Import `Depends`
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
### Declare the dependency, in the "dependant"
|
||||
|
||||
The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter:
|
||||
|
||||
```Python hl_lines="13 18"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13 18"
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="11 16"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
Although you use `Depends` in the parameters of your function the same way you use `Body`, `Query`, etc, `Depends` works a bit differently.
|
||||
|
||||
|
||||
@@ -6,25 +6,41 @@ They can be as **deep** as you need them to be.
|
||||
|
||||
**FastAPI** will take care of solving them.
|
||||
|
||||
### First dependency "dependable"
|
||||
## First dependency "dependable"
|
||||
|
||||
You could create a first dependency ("dependable") like:
|
||||
|
||||
```Python hl_lines="8-9"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8-9"
|
||||
{!> ../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6-7"
|
||||
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
It declares an optional query parameter `q` as a `str`, and then it just returns it.
|
||||
|
||||
This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.
|
||||
|
||||
### Second dependency, "dependable" and "dependant"
|
||||
## Second dependency, "dependable" and "dependant"
|
||||
|
||||
Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too):
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!> ../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
Let's focus on the parameters declared:
|
||||
|
||||
@@ -33,13 +49,21 @@ Let's focus on the parameters declared:
|
||||
* It also declares an optional `last_query` cookie, as a `str`.
|
||||
* If the user didn't provide any query `q`, we use the last query used, which we saved to a cookie before.
|
||||
|
||||
### Use the dependency
|
||||
## Use the dependency
|
||||
|
||||
Then we can use the dependency with:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Notice that we are only declaring one dependency in the *path operation function*, the `query_or_cookie_extractor`.
|
||||
|
||||
@@ -20,9 +20,17 @@ You can use `jsonable_encoder` for that.
|
||||
|
||||
It receives an object, like a Pydantic model, and returns a JSON compatible version:
|
||||
|
||||
```Python hl_lines="5 22"
|
||||
{!../../../docs_src/encoder/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="5 22"
|
||||
{!> ../../../docs_src/encoder/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="4 21"
|
||||
{!> ../../../docs_src/encoder/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`.
|
||||
|
||||
|
||||
@@ -55,12 +55,28 @@ Here are some of the additional data types you can use:
|
||||
|
||||
Here's an example *path operation* with parameters using some of the above types.
|
||||
|
||||
```Python hl_lines="1 3 12-16"
|
||||
{!../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 3 12-16"
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 2 11-15"
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like:
|
||||
|
||||
```Python hl_lines="18-19"
|
||||
{!../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18-19"
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
@@ -17,9 +17,17 @@ This is especially the case for user models, because:
|
||||
|
||||
Here's a general idea of how the models could look like with their password fields and the places where they are used:
|
||||
|
||||
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
|
||||
{!../../../docs_src/extra_models/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
|
||||
{!> ../../../docs_src/extra_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39"
|
||||
{!> ../../../docs_src/extra_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
### About `**user_in.dict()`
|
||||
|
||||
@@ -150,9 +158,17 @@ All the data conversion, validation, documentation, etc. will still work as norm
|
||||
|
||||
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
|
||||
|
||||
```Python hl_lines="9 15-16 19-20 23-24"
|
||||
{!../../../docs_src/extra_models/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 15-16 19-20 23-24"
|
||||
{!> ../../../docs_src/extra_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 13-14 17-18 21-22"
|
||||
{!> ../../../docs_src/extra_models/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## `Union` or `anyOf`
|
||||
|
||||
@@ -165,19 +181,49 @@ To do that, use the standard Python type hint <a href="https://docs.python.org/3
|
||||
!!! note
|
||||
When defining a <a href="https://pydantic-docs.helpmanual.io/usage/types/#unions" class="external-link" target="_blank">`Union`</a>, include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`.
|
||||
|
||||
```Python hl_lines="1 14-15 18-20 33"
|
||||
{!../../../docs_src/extra_models/tutorial003.py!}
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 14-15 18-20 33"
|
||||
{!> ../../../docs_src/extra_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 14-15 18-20 33"
|
||||
{!> ../../../docs_src/extra_models/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### `Union` in Python 3.10
|
||||
|
||||
In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`.
|
||||
|
||||
Because we are passing it as a **value to an argument** instead of putting it in a **type annotation**, we have to use `Union` even in Python 3.10.
|
||||
|
||||
If it was in a type annotation we could have used the vertical bar, as:
|
||||
|
||||
```Python
|
||||
some_variable: PlaneItem | CarItem
|
||||
```
|
||||
|
||||
But if we put that in `response_model=PlaneItem | CarItem` we would get an error, because Python would try to perform an **invalid operation** between `PlaneItem` and `CarItem` instead of interpreting that as a type annotation.
|
||||
|
||||
## List of models
|
||||
|
||||
The same way, you can declare responses of lists of objects.
|
||||
|
||||
For that, use the standard Python `typing.List`:
|
||||
For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above):
|
||||
|
||||
```Python hl_lines="1 20"
|
||||
{!../../../docs_src/extra_models/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 20"
|
||||
{!> ../../../docs_src/extra_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/extra_models/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
## Response with arbitrary `dict`
|
||||
|
||||
@@ -185,11 +231,19 @@ You can also declare a response using a plain arbitrary `dict`, declaring just t
|
||||
|
||||
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
|
||||
|
||||
In this case, you can use `typing.Dict`:
|
||||
In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above):
|
||||
|
||||
```Python hl_lines="1 8"
|
||||
{!../../../docs_src/extra_models/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 8"
|
||||
{!> ../../../docs_src/extra_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!> ../../../docs_src/extra_models/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
|
||||
@@ -260,6 +260,4 @@ You can import and re-use the default exception handlers from `fastapi.exception
|
||||
{!../../../docs_src/handling_errors/tutorial006.py!}
|
||||
```
|
||||
|
||||
In this example, you are just `print`ing the error with a very expressive message.
|
||||
|
||||
But you get the idea, you can use the exception and then just re-use the default exception handlers.
|
||||
In this example you are just `print`ing the error with a very expressive message, but you get the idea. You can use the exception and then just re-use the default exception handlers.
|
||||
|
||||
@@ -6,9 +6,17 @@ You can define Header parameters the same way you define `Query`, `Path` and `Co
|
||||
|
||||
First import `Header`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!> ../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Declare `Header` parameters
|
||||
|
||||
@@ -16,9 +24,17 @@ Then declare the header parameters using the same structure as with `Path`, `Que
|
||||
|
||||
The first value is the default value, you can pass all the extra validation or annotation parameters:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
`Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class.
|
||||
@@ -44,14 +60,21 @@ So, you can use `user_agent` as you normally would in Python code, instead of ne
|
||||
|
||||
If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/header_params/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/header_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/header_params/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores.
|
||||
|
||||
|
||||
## Duplicate headers
|
||||
|
||||
It is possible to receive duplicate headers. That means, the same header with multiple values.
|
||||
@@ -62,9 +85,23 @@ You will receive all the values from the duplicate header as a Python `list`.
|
||||
|
||||
For example, to declare a header of `X-Token` that can appear more than once, you can write:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/header_params/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/header_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/header_params/tutorial003_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/header_params/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
If you communicate with that *path operation* sending two HTTP headers like:
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ For the tutorial, you might want to install it with all the optional dependencie
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi[all]
|
||||
$ pip install "fastapi[all]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
@@ -64,7 +64,7 @@ $ pip install fastapi[all]
|
||||
Also install `uvicorn` to work as the server:
|
||||
|
||||
```
|
||||
pip install uvicorn[standard]
|
||||
pip install "uvicorn[standard]"
|
||||
```
|
||||
|
||||
And the same for each of the optional dependencies that you want to use.
|
||||
|
||||
@@ -13,9 +13,23 @@ 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 in `status`:
|
||||
|
||||
```Python hl_lines="3 17"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3 17"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="3 17"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 15"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
That status code will be used in the response and will be added to the OpenAPI schema.
|
||||
|
||||
@@ -28,9 +42,23 @@ That status code will be used in the response and will be added to the OpenAPI s
|
||||
|
||||
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15 20 25"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
They will be added to the OpenAPI schema and used by the automatic documentation interfaces:
|
||||
|
||||
@@ -40,9 +68,23 @@ They will be added to the OpenAPI schema and used by the automatic documentation
|
||||
|
||||
You can add a `summary` and `description`:
|
||||
|
||||
```Python hl_lines="20-21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="20-21"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="20-21"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="18-19"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
## Description from docstring
|
||||
|
||||
@@ -50,9 +92,23 @@ As descriptions tend to be long and cover multiple lines, you can declare the *p
|
||||
|
||||
You can write <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
|
||||
|
||||
```Python hl_lines="19-27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19-27"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="19-27"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-25"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
It will be used in the interactive docs:
|
||||
|
||||
@@ -62,9 +118,23 @@ It will be used in the interactive docs:
|
||||
|
||||
You can specify the response description with the parameter `response_description`:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Notice that `response_description` refers specifically to the response, the `description` refers to the *path operation* in general.
|
||||
|
||||
@@ -6,9 +6,17 @@ The same way you can declare more validations and metadata for query parameters
|
||||
|
||||
First, import `Path` from `fastapi`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Declare metadata
|
||||
|
||||
@@ -16,13 +24,21 @@ You can declare all the same parameters as for `Query`.
|
||||
|
||||
For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
A path parameter is always required as it has to be part of the path.
|
||||
|
||||
|
||||
So, you should declare it with `...` to mark it as required.
|
||||
|
||||
Nevertheless, even if you declared it with `None` or set a default value, it would not affect anything, it would still be always required.
|
||||
|
||||
@@ -4,11 +4,19 @@
|
||||
|
||||
Let's take this application as example:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
The query parameter `q` is of type `Optional[str]`, that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
The query parameter `q` is of type `Optional[str]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
@@ -23,17 +31,33 @@ We are going to enforce that even though `q` is optional, whenever it is provide
|
||||
|
||||
To achieve that, first import `Query` from `fastapi`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Use `Query` as the default value
|
||||
|
||||
And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
As we have to replace the default value `None` with `Query(None)`, the first parameter to `Query` serves the same purpose of defining that default value.
|
||||
|
||||
@@ -49,10 +73,22 @@ q: Optional[str] = Query(None)
|
||||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
And in Python 3.10 and above:
|
||||
|
||||
```Python
|
||||
q: str | None = Query(None)
|
||||
```
|
||||
|
||||
...makes the parameter optional, the same as:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
But it declares it explicitly as being a query parameter.
|
||||
|
||||
!!! info
|
||||
Have in mind that FastAPI cares about the part:
|
||||
Have in mind that the most important part to make a parameter optional is the part:
|
||||
|
||||
```Python
|
||||
= None
|
||||
@@ -64,9 +100,9 @@ But it declares it explicitly as being a query parameter.
|
||||
= Query(None)
|
||||
```
|
||||
|
||||
and will use that `None` to detect that the query parameter is not required.
|
||||
as it will use that `None` as the default value, and that way make the parameter **not required**.
|
||||
|
||||
The `Optional` part is only to allow your editor to provide better support.
|
||||
The `Optional` part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required.
|
||||
|
||||
Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings:
|
||||
|
||||
@@ -80,17 +116,33 @@ This will validate the data, show a clear error when the data is not valid, and
|
||||
|
||||
You can also add a parameter `min_length`:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
## Add regular expressions
|
||||
|
||||
You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> that the parameter should match:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
This specific regular expression checks that the received parameter value:
|
||||
|
||||
@@ -152,9 +204,23 @@ When you define a query parameter explicitly with `Query` you can also declare i
|
||||
|
||||
For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial011.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial011.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
Then, with a URL like:
|
||||
|
||||
@@ -186,9 +252,17 @@ The interactive API docs will update accordingly, to allow multiple values:
|
||||
|
||||
And you can also define a default `list` of values if none are provided:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial012.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!}
|
||||
```
|
||||
|
||||
If you go to:
|
||||
|
||||
@@ -209,7 +283,7 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
|
||||
|
||||
#### Using `list`
|
||||
|
||||
You can also use `list` directly instead of `List[str]`:
|
||||
You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial013.py!}
|
||||
@@ -233,15 +307,31 @@ That information will be included in the generated OpenAPI and used by the docum
|
||||
|
||||
You can add a `title`:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial007.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!}
|
||||
```
|
||||
|
||||
And a `description`:
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial008.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
|
||||
```
|
||||
|
||||
## Alias parameters
|
||||
|
||||
@@ -261,9 +351,17 @@ But you still need it to be exactly `item-query`...
|
||||
|
||||
Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial009.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
## Deprecating parameters
|
||||
|
||||
@@ -273,9 +371,17 @@ You have to leave it there a while because there are clients using it, but you w
|
||||
|
||||
Then pass the parameter `deprecated=True` to `Query`:
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial010.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
|
||||
```
|
||||
|
||||
The docs will show it like this:
|
||||
|
||||
|
||||
@@ -63,27 +63,38 @@ The parameter values in your function will be:
|
||||
|
||||
The same way, you can declare optional query parameters, by setting their default to `None`:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
In this case, the function parameter `q` will be optional, and will be `None` by default.
|
||||
|
||||
!!! check
|
||||
Also notice that **FastAPI** is smart enough to notice that the path parameter `item_id` is a path parameter and `q` is not, so, it's a query parameter.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that `q` is optional because of the `= None`.
|
||||
|
||||
The `Optional` in `Optional[str]` is not used by FastAPI (FastAPI will only use the `str` part), but the `Optional[str]` will let your editor help you finding errors in your code.
|
||||
|
||||
## Query parameter type conversion
|
||||
|
||||
You can also declare `bool` types, and they will be converted:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
In this case, if you go to:
|
||||
|
||||
@@ -126,9 +137,17 @@ And you don't have to declare them in any specific order.
|
||||
|
||||
They will be detected by name:
|
||||
|
||||
```Python hl_lines="8 10"
|
||||
{!../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8 10"
|
||||
{!> ../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6 8"
|
||||
{!> ../../../docs_src/query_params/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
## Required query parameters
|
||||
|
||||
@@ -184,9 +203,17 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
|
||||
And of course, you can define some parameters as required, some as having a default value, and some entirely optional:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params/tutorial006.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/query_params/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/query_params/tutorial006_py310.py!}
|
||||
```
|
||||
|
||||
In this case, there are 3 query parameters:
|
||||
|
||||
|
||||
@@ -119,21 +119,22 @@ It's possible to upload several files at the same time.
|
||||
|
||||
They would be associated to the same "form field" sent using "form data".
|
||||
|
||||
To use that, declare a `List` of `bytes` or `UploadFile`:
|
||||
To use that, declare a list of `bytes` or `UploadFile`:
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
{!../../../docs_src/request_files/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
{!> ../../../docs_src/request_files/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="8 13"
|
||||
{!> ../../../docs_src/request_files/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||
|
||||
!!! note
|
||||
Notice that, as of 2019-04-14, Swagger UI doesn't support multiple file uploads in the same form field. For more information, check <a href="https://github.com/swagger-api/swagger-ui/issues/4276" class="external-link" target="_blank">#4276</a> and <a href="https://github.com/swagger-api/swagger-ui/issues/3641" class="external-link" target="_blank">#3641</a>.
|
||||
|
||||
Nevertheless, **FastAPI** is already compatible with it, using the standard OpenAPI.
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
@@ -8,9 +8,23 @@ You can declare the model used for the response with the parameter `response_mod
|
||||
* `@app.delete()`
|
||||
* etc.
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!../../../docs_src/response_model/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/response_model/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/response_model/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!> ../../../docs_src/response_model/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body.
|
||||
@@ -35,15 +49,31 @@ But most importantly:
|
||||
|
||||
Here we are declaring a `UserIn` model, it will contain a plaintext password:
|
||||
|
||||
```Python hl_lines="9 11"
|
||||
{!../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 11"
|
||||
{!> ../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 9"
|
||||
{!> ../../../docs_src/response_model/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
And we are using this model to declare our input and the same model to declare our output:
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!> ../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15-16"
|
||||
{!> ../../../docs_src/response_model/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
|
||||
|
||||
@@ -58,21 +88,45 @@ But if we use the same model for another *path operation*, we could be sending o
|
||||
|
||||
We can instead create an input model with the plaintext password and an output model without it:
|
||||
|
||||
```Python hl_lines="9 11 16"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 11 16"
|
||||
{!> ../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 9 14"
|
||||
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
Here, even though our *path operation function* is returning the same input user that contains the password:
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!> ../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!> ../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic).
|
||||
|
||||
@@ -90,9 +144,23 @@ And both models will be used for the interactive API documentation:
|
||||
|
||||
Your response model could have default values, like:
|
||||
|
||||
```Python hl_lines="11 13-14"
|
||||
{!../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="11 13-14"
|
||||
{!> ../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="11 13-14"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="9 11-12"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
* `description: Optional[str] = None` has a default of `None`.
|
||||
* `tax: float = 10.5` has a default of `10.5`.
|
||||
@@ -106,9 +174,23 @@ For example, if you have models with many optional attributes in a NoSQL databas
|
||||
|
||||
You can set the *path operation decorator* parameter `response_model_exclude_unset=True`:
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!> ../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
and those default values won't be included in the response, only the values actually set.
|
||||
|
||||
@@ -185,9 +267,17 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
|
||||
|
||||
This also applies to `response_model_by_alias` that works similarly.
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!../../../docs_src/response_model/tutorial005.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!> ../../../docs_src/response_model/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
{!> ../../../docs_src/response_model/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The syntax `{"name", "description"}` creates a `set` with those two values.
|
||||
@@ -198,9 +288,17 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
|
||||
|
||||
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!../../../docs_src/response_model/tutorial006.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!> ../../../docs_src/response_model/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
{!> ../../../docs_src/response_model/tutorial006_py310.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
|
||||
@@ -8,9 +8,17 @@ Here are several ways to do it.
|
||||
|
||||
You can declare an `example` for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>:
|
||||
|
||||
```Python hl_lines="15-23"
|
||||
{!../../../docs_src/schema_extra_example/tutorial001.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15-23"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="13-21"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs.
|
||||
|
||||
@@ -25,9 +33,17 @@ When using `Field()` with Pydantic models, you can also declare extra info for t
|
||||
|
||||
You can use this to add `example` for each field:
|
||||
|
||||
```Python hl_lines="4 10-13"
|
||||
{!../../../docs_src/schema_extra_example/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4 10-13"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2 8-11"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Keep in mind that those extra arguments passed won't add any validation, only extra information, for documentation purposes.
|
||||
@@ -50,9 +66,17 @@ you can also declare a data `example` or a group of `examples` with additional i
|
||||
|
||||
Here we pass an `example` of the data expected in `Body()`:
|
||||
|
||||
```Python hl_lines="21-26"
|
||||
{!../../../docs_src/schema_extra_example/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21-26"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19-24"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### Example in the docs UI
|
||||
|
||||
@@ -73,9 +97,17 @@ Each specific example `dict` in the `examples` can contain:
|
||||
* `value`: This is the actual example shown, e.g. a `dict`.
|
||||
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`.
|
||||
|
||||
```Python hl_lines="22-48"
|
||||
{!../../../docs_src/schema_extra_example/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="22-48"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="20-46"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
### Examples in the docs UI
|
||||
|
||||
|
||||
@@ -16,9 +16,17 @@ First, let's create a Pydantic user model.
|
||||
|
||||
The same way we use Pydantic to declare bodies, we can use it anywhere else:
|
||||
|
||||
```Python hl_lines="5 12-16"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="5 12-16"
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="3 10-14"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Create a `get_current_user` dependency
|
||||
|
||||
@@ -30,25 +38,49 @@ Remember that dependencies can have sub-dependencies?
|
||||
|
||||
The same as we were doing before in the *path operation* directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`:
|
||||
|
||||
```Python hl_lines="25"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="25"
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="23"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Get the user
|
||||
|
||||
`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model:
|
||||
|
||||
```Python hl_lines="19-22 26-27"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19-22 26-27"
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-20 24-25"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Inject the current user
|
||||
|
||||
So now we can use the same `Depends` with our `get_current_user` in the *path operation*:
|
||||
|
||||
```Python hl_lines="31"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="31"
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="29"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
Notice that we declare the type of `current_user` as the Pydantic model `User`.
|
||||
|
||||
@@ -64,7 +96,6 @@ This will help us inside of the function with all the completion and type checks
|
||||
|
||||
We are not restricted to having only one dependency that can return that type of data.
|
||||
|
||||
|
||||
## Other models
|
||||
|
||||
You can now get the current user directly in the *path operation functions* and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`.
|
||||
@@ -81,10 +112,9 @@ You actually don't have users that log in to your application but robots, bots,
|
||||
|
||||
Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system.
|
||||
|
||||
|
||||
## Code size
|
||||
|
||||
This example might seem verbose. Have in mind that we are mixing security, data models utility functions and *path operations* in the same file.
|
||||
This example might seem verbose. Have in mind that we are mixing security, data models, utility functions and *path operations* in the same file.
|
||||
|
||||
But here's the key point.
|
||||
|
||||
@@ -98,9 +128,17 @@ And all of them (or any portion of them that you want) can take the advantage of
|
||||
|
||||
And all these thousands of *path operations* can be as small as 3 lines:
|
||||
|
||||
```Python hl_lines="30-32"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="30-32"
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="28-30"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ We need to install `python-jose` to generate and verify the JWT tokens in Python
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install python-jose[cryptography]
|
||||
$ pip install "python-jose[cryptography]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
@@ -76,7 +76,7 @@ So, install PassLib with Bcrypt:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install passlib[bcrypt]
|
||||
$ pip install "passlib[bcrypt]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
@@ -109,9 +109,17 @@ 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 48 55-56 59-60 69-75"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="7 48 55-56 59-60 69-75"
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6 47 54-55 58-59 68-74"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`.
|
||||
@@ -144,9 +152,17 @@ 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="6 12-14 28-30 78-86"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="6 12-14 28-30 78-86"
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="5 11-13 27-29 77-85"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
## Update the dependencies
|
||||
|
||||
@@ -156,9 +172,17 @@ 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="89-106"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="89-106"
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="88-105"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
## Update the `/token` *path operation*
|
||||
|
||||
@@ -166,9 +190,17 @@ Create a `timedelta` with the expiration time of the token.
|
||||
|
||||
Create a real JWT access token and return it.
|
||||
|
||||
```Python hl_lines="115-128"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="115-128"
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="114-127"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
### Technical details about the JWT "subject" `sub`
|
||||
|
||||
|
||||
@@ -49,9 +49,17 @@ Now let's use the utilities provided by **FastAPI** to handle this.
|
||||
|
||||
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`:
|
||||
|
||||
```Python hl_lines="4 76"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4 76"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2 74"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
`OAuth2PasswordRequestForm` is a class dependency that declares a form body with:
|
||||
|
||||
@@ -90,9 +98,17 @@ 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="3 77-79"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3 77-79"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 75-77"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### Check the password
|
||||
|
||||
@@ -118,9 +134,17 @@ 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="80-83"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="80-83"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="78-81"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
#### About `**user_dict`
|
||||
|
||||
@@ -156,9 +180,17 @@ 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="85"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="85"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="83"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
By the spec, you should return a JSON with an `access_token` and a `token_type`, the same as in this example.
|
||||
@@ -181,9 +213,17 @@ 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="58-67 69-72 90"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="58-66 69-72 90"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="55-64 67-70 88"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
The additional header `WWW-Authenticate` with value `Bearer` we are returning here is also part of the spec.
|
||||
|
||||
@@ -248,9 +248,23 @@ So, the user will also have a `password` when creating it.
|
||||
|
||||
But for security, the `password` won't be in other Pydantic *models*, for example, it won't be sent from the API when reading a user.
|
||||
|
||||
```Python hl_lines="3 6-8 11-12 23-24 27-28"
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3 6-8 11-12 23-24 27-28"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="3 6-8 11-12 23-24 27-28"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 4-6 9-10 21-22 25-26"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
#### SQLAlchemy style and Pydantic style
|
||||
|
||||
@@ -278,9 +292,23 @@ The same way, when reading a user, we can now declare that `items` will contain
|
||||
|
||||
Not only the IDs of those items, but all the data that we defined in the Pydantic *model* for reading items: `Item`.
|
||||
|
||||
```Python hl_lines="15-17 31-34"
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15-17 31-34"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="15-17 31-34"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="13-15 29-32"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Notice that the `User`, the Pydantic *model* that will be used when reading a user (returning it from the API) doesn't include the `password`.
|
||||
@@ -293,9 +321,23 @@ This <a href="https://pydantic-docs.helpmanual.io/#config" class="external-link"
|
||||
|
||||
In the `Config` class, set the attribute `orm_mode = True`.
|
||||
|
||||
```Python hl_lines="15 19-20 31 36-37"
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15 19-20 31 36-37"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="15 19-20 31 36-37"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="13 17-18 29 34-35"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Notice it's assigning a value with `=`, like:
|
||||
@@ -425,9 +467,17 @@ 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"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
#### Alembic Note
|
||||
|
||||
@@ -441,17 +491,6 @@ You can find an example of Alembic in a FastAPI project in the templates from [P
|
||||
|
||||
### Create a dependency
|
||||
|
||||
!!! info
|
||||
For this to work, you need to use **Python 3.7** or above, or in **Python 3.6**, install the "backports":
|
||||
|
||||
```console
|
||||
$ pip install async-exit-stack async-generator
|
||||
```
|
||||
|
||||
This installs <a href="https://github.com/sorcio/async_exit_stack" class="external-link" target="_blank">async-exit-stack</a> and <a href="https://github.com/python-trio/async_generator" class="external-link" target="_blank">async-generator</a>.
|
||||
|
||||
You can also use the alternative method with a "middleware" explained at the end.
|
||||
|
||||
Now use the `SessionLocal` class we created in the `sql_app/databases.py` file to create a dependency.
|
||||
|
||||
We need to have an independent database session/connection (`SessionLocal`) per request, use the same session through all the request and then close it after the request is finished.
|
||||
@@ -462,9 +501,17 @@ For that, we will create a new dependency with `yield`, as explained before in t
|
||||
|
||||
Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in a single request, and then close it once the request is finished.
|
||||
|
||||
```Python hl_lines="15-20"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15-20"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="13-18"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
We put the creation of the `SessionLocal()` and handling of the requests in a `try` block.
|
||||
@@ -479,9 +526,17 @@ And then, when using the dependency in a *path operation function*, we declare i
|
||||
|
||||
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`:
|
||||
|
||||
```Python hl_lines="24 32 38 47 53"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="24 32 38 47 53"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="22 30 36 45 51"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
!!! info "Technical Details"
|
||||
The parameter `db` is actually of type `SessionLocal`, but this class (created with `sessionmaker()`) is a "proxy" of a SQLAlchemy `Session`, so, the editor doesn't really know what methods are provided.
|
||||
@@ -492,9 +547,17 @@ This will then give us better editor support inside the *path operation function
|
||||
|
||||
Now, finally, here's the standard **FastAPI** *path operations* code.
|
||||
|
||||
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="21-26 29-32 35-40 43-47 50-53"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
We are creating the database session before each request in the dependency with `yield`, and then closing it afterwards.
|
||||
|
||||
@@ -577,9 +640,23 @@ For example, in a background task worker with <a href="https://docs.celeryprojec
|
||||
|
||||
* `sql_app/schemas.py`:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
* `sql_app/crud.py`:
|
||||
|
||||
@@ -589,9 +666,17 @@ For example, in a background task worker with <a href="https://docs.celeryprojec
|
||||
|
||||
* `sql_app/main.py`:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
## Check it
|
||||
|
||||
@@ -640,9 +725,17 @@ 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="14-22"
|
||||
{!../../../docs_src/sql_databases/sql_app/alt_main.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="14-22"
|
||||
{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="12-20"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
We put the creation of the `SessionLocal()` and handling of the requests in a `try` block.
|
||||
|
||||
@@ -2,20 +2,6 @@
|
||||
|
||||
You can serve static files automatically from a directory using `StaticFiles`.
|
||||
|
||||
## Install `aiofiles`
|
||||
|
||||
First you need to install `aiofiles`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install aiofiles
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Use `StaticFiles`
|
||||
|
||||
* Import `StaticFiles`.
|
||||
|
||||
@@ -10,7 +10,7 @@ With it, you can use <a href="https://docs.pytest.org/" class="external-link" ta
|
||||
|
||||
Import `TestClient`.
|
||||
|
||||
Create a `TestClient` passing to it your **FastAPI**.
|
||||
Create a `TestClient` passing to it your **FastAPI** application.
|
||||
|
||||
Create functions with a name that starts with `test_` (this is standard `pytest` conventions).
|
||||
|
||||
@@ -65,7 +65,7 @@ Now let's extend this example and add more details to see how to test different
|
||||
|
||||
### Extended **FastAPI** app file
|
||||
|
||||
Let's say you have a file `main_b.py` with your **FastAPI** app.
|
||||
Let's say that now the file `main.py` with your **FastAPI** app has some other **path operations**.
|
||||
|
||||
It has a `GET` operation that could return an error.
|
||||
|
||||
@@ -73,16 +73,24 @@ It has a `POST` operation that could return several errors.
|
||||
|
||||
Both *path operations* require an `X-Token` header.
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/app_testing/main_b.py!}
|
||||
```
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
|
||||
```
|
||||
|
||||
### Extended testing file
|
||||
|
||||
You could then have a `test_main_b.py`, the same as before, with the extended tests:
|
||||
You could then update `test_main.py` with the extended tests:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/app_testing/test_main_b.py!}
|
||||
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
|
||||
```
|
||||
|
||||
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `requests`.
|
||||
|
||||
@@ -9,13 +9,13 @@ theme:
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb-outline
|
||||
icon: material/lightbulb
|
||||
name: Switch to light mode
|
||||
- scheme: slate
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb
|
||||
icon: material/lightbulb-outline
|
||||
name: Switch to dark mode
|
||||
features:
|
||||
- search.suggest
|
||||
@@ -29,9 +29,6 @@ theme:
|
||||
repo_name: tiangolo/fastapi
|
||||
repo_url: https://github.com/tiangolo/fastapi
|
||||
edit_uri: ''
|
||||
google_analytics:
|
||||
- UA-133183413-1
|
||||
- auto
|
||||
plugins:
|
||||
- search
|
||||
- markdownextradata:
|
||||
@@ -40,6 +37,7 @@ nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- az: /az/
|
||||
- de: /de/
|
||||
- es: /es/
|
||||
- fr: /fr/
|
||||
@@ -147,9 +145,11 @@ nav:
|
||||
- deployment/index.md
|
||||
- deployment/versions.md
|
||||
- deployment/https.md
|
||||
- deployment/deta.md
|
||||
- deployment/docker.md
|
||||
- deployment/manually.md
|
||||
- deployment/concepts.md
|
||||
- deployment/deta.md
|
||||
- deployment/server-workers.md
|
||||
- deployment/docker.md
|
||||
- project-generation.md
|
||||
- alternatives.md
|
||||
- history-design-future.md
|
||||
@@ -163,9 +163,6 @@ markdown_extensions:
|
||||
permalink: true
|
||||
- markdown.extensions.codehilite:
|
||||
guess_lang: false
|
||||
# Uncomment these 2 lines during development to more easily add highlights
|
||||
# - pymdownx.highlight:
|
||||
# linenums: true
|
||||
- mdx_include:
|
||||
base_path: docs
|
||||
- admonition
|
||||
@@ -176,8 +173,12 @@ markdown_extensions:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format ''
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
extra:
|
||||
analytics:
|
||||
provider: google
|
||||
property: UA-133183413-1
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/fastapi
|
||||
@@ -196,6 +197,8 @@ extra:
|
||||
alternate:
|
||||
- link: /
|
||||
name: en - English
|
||||
- link: /az/
|
||||
name: az
|
||||
- link: /de/
|
||||
name: de
|
||||
- link: /es/
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
<img class="sponsor-image" src="/img/sponsors/jina-banner.svg" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a title="CryptAPI: Your easy to use, secure and privacy oriented payment gateway." style="display: block; position: relative;" href="https://cryptapi.io/" target="_blank">
|
||||
<span class="sponsor-badge">sponsor</span>
|
||||
<img class="sponsor-image" src="/img/sponsors/cryptapi-banner.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -4,9 +4,7 @@ Este tutorial te muestra cómo usar **FastAPI** con la mayoría de sus caracter
|
||||
|
||||
Cada sección se basa gradualmente en las anteriores, pero está estructurada en temas separados, así puedes ir directamente a cualquier tema en concreto para resolver tus necesidades específicas sobre la API.
|
||||
|
||||
También está diseñado para funcionar como una referencia futura.
|
||||
|
||||
Para que puedas volver y ver exactamente lo que necesitas.
|
||||
Funciona también como una referencia futura, para que puedas volver y ver exactamente lo que necesitas.
|
||||
|
||||
## Ejecuta el código
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ theme:
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb-outline
|
||||
icon: material/lightbulb
|
||||
name: Switch to light mode
|
||||
- scheme: slate
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb
|
||||
icon: material/lightbulb-outline
|
||||
name: Switch to dark mode
|
||||
features:
|
||||
- search.suggest
|
||||
@@ -29,9 +29,6 @@ theme:
|
||||
repo_name: tiangolo/fastapi
|
||||
repo_url: https://github.com/tiangolo/fastapi
|
||||
edit_uri: ''
|
||||
google_analytics:
|
||||
- UA-133183413-1
|
||||
- auto
|
||||
plugins:
|
||||
- search
|
||||
- markdownextradata:
|
||||
@@ -40,6 +37,7 @@ nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- az: /az/
|
||||
- de: /de/
|
||||
- es: /es/
|
||||
- fr: /fr/
|
||||
@@ -79,8 +77,12 @@ markdown_extensions:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format ''
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
extra:
|
||||
analytics:
|
||||
provider: google
|
||||
property: UA-133183413-1
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/fastapi
|
||||
@@ -99,6 +101,8 @@ extra:
|
||||
alternate:
|
||||
- link: /
|
||||
name: en - English
|
||||
- link: /az/
|
||||
name: az
|
||||
- link: /de/
|
||||
name: de
|
||||
- link: /es/
|
||||
|
||||
182
docs/fr/docs/deployment/docker.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Déployer avec Docker
|
||||
|
||||
Dans cette section, vous verrez des instructions et des liens vers des guides pour savoir comment :
|
||||
|
||||
* Faire de votre application **FastAPI** une image/conteneur Docker avec une performance maximale. En environ **5 min**.
|
||||
* (Optionnellement) comprendre ce que vous, en tant que développeur, devez savoir sur HTTPS.
|
||||
* Configurer un cluster en mode Docker Swarm avec HTTPS automatique, même sur un simple serveur à 5 dollars US/mois. En environ **20 min**.
|
||||
* Générer et déployer une application **FastAPI** complète, en utilisant votre cluster Docker Swarm, avec HTTPS, etc. En environ **10 min**.
|
||||
|
||||
Vous pouvez utiliser <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> pour le déploiement. Il présente plusieurs avantages comme la sécurité, la réplicabilité, la simplicité de développement, etc.
|
||||
|
||||
Si vous utilisez Docker, vous pouvez utiliser l'image Docker officielle :
|
||||
|
||||
## <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
|
||||
|
||||
Cette image est dotée d'un mécanisme d'"auto-tuning", de sorte qu'il vous suffit d'ajouter votre code pour obtenir automatiquement des performances très élevées. Et sans faire de sacrifices.
|
||||
|
||||
Mais vous pouvez toujours changer et mettre à jour toutes les configurations avec des variables d'environnement ou des fichiers de configuration.
|
||||
|
||||
!!! tip "Astuce"
|
||||
Pour voir toutes les configurations et options, rendez-vous sur la page de l'image Docker : <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
## Créer un `Dockerfile`
|
||||
|
||||
* Allez dans le répertoire de votre projet.
|
||||
* Créez un `Dockerfile` avec :
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
|
||||
COPY ./app /app
|
||||
```
|
||||
|
||||
### Applications plus larges
|
||||
|
||||
Si vous avez suivi la section sur la création d' [Applications avec plusieurs fichiers](../tutorial/bigger-applications.md){.internal-link target=_blank}, votre `Dockerfile` pourrait ressembler à ceci :
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
|
||||
COPY ./app /app/app
|
||||
```
|
||||
|
||||
### Raspberry Pi et autres architectures
|
||||
|
||||
Si vous utilisez Docker sur un Raspberry Pi (qui a un processeur ARM) ou toute autre architecture, vous pouvez créer un `Dockerfile` à partir de zéro, basé sur une image de base Python (qui est multi-architecture) et utiliser Uvicorn seul.
|
||||
|
||||
Dans ce cas, votre `Dockerfile` pourrait ressembler à ceci :
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.7
|
||||
|
||||
RUN pip install fastapi uvicorn
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
COPY ./app /app
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
## Créer le code **FastAPI**.
|
||||
|
||||
* Créer un répertoire `app` et y entrer.
|
||||
* Créez un fichier `main.py` avec :
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
* Vous devriez maintenant avoir une structure de répertoire telle que :
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ └── main.py
|
||||
└── Dockerfile
|
||||
```
|
||||
|
||||
## Construire l'image Docker
|
||||
|
||||
* Allez dans le répertoire du projet (dans lequel se trouve votre `Dockerfile`, contenant votre répertoire `app`).
|
||||
* Construisez votre image FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ docker build -t myimage .
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Démarrer le conteneur Docker
|
||||
|
||||
* Exécutez un conteneur basé sur votre image :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ docker run -d --name mycontainer -p 80:80 myimage
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Vous disposez maintenant d'un serveur FastAPI optimisé dans un conteneur Docker. Configuré automatiquement pour votre
|
||||
serveur actuel (et le nombre de cœurs du CPU).
|
||||
|
||||
## Vérifier
|
||||
|
||||
Vous devriez pouvoir accéder à votre application via l'URL de votre conteneur Docker, par exemple : <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez quelque chose comme :
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
## Documentation interactive de l'API
|
||||
|
||||
Vous pouvez maintenant visiter <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez la documentation interactive automatique de l'API (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
|
||||
|
||||

|
||||
|
||||
## Documentation de l'API alternative
|
||||
|
||||
Et vous pouvez également aller sur <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> ou <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez la documentation automatique alternative (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
|
||||

|
||||
|
||||
## Traefik
|
||||
|
||||
<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a> est un reverse proxy/load balancer
|
||||
haute performance. Il peut faire office de "Proxy de terminaison TLS" (entre autres fonctionnalités).
|
||||
|
||||
Il est intégré à Let's Encrypt. Ainsi, il peut gérer toutes les parties HTTPS, y compris l'acquisition et le renouvellement des certificats.
|
||||
|
||||
Il est également intégré à Docker. Ainsi, vous pouvez déclarer vos domaines dans les configurations de chaque application et faire en sorte qu'elles lisent ces configurations, génèrent les certificats HTTPS et servent via HTTPS à votre application automatiquement, sans nécessiter aucune modification de leurs configurations.
|
||||
|
||||
---
|
||||
|
||||
Avec ces informations et ces outils, passez à la section suivante pour tout combiner.
|
||||
|
||||
## Cluster en mode Docker Swarm avec Traefik et HTTPS
|
||||
|
||||
Vous pouvez avoir un cluster en mode Docker Swarm configuré en quelques minutes (environ 20 min) avec un processus Traefik principal gérant HTTPS (y compris l'acquisition et le renouvellement des certificats).
|
||||
|
||||
En utilisant le mode Docker Swarm, vous pouvez commencer par un "cluster" d'une seule machine (il peut même s'agir
|
||||
d'un serveur à 5 USD/mois) et ensuite vous pouvez vous développer autant que vous le souhaitez en ajoutant d'autres serveurs.
|
||||
|
||||
Pour configurer un cluster en mode Docker Swarm avec Traefik et la gestion de HTTPS, suivez ce guide :
|
||||
|
||||
### <a href="https://medium.com/@tiangolo/docker-swarm-mode-and-traefik-for-a-https-cluster-20328dba6232" class="external-link" target="_blank">Docker Swarm Mode et Traefik pour un cluster HTTPS</a>
|
||||
|
||||
### Déployer une application FastAPI
|
||||
|
||||
La façon la plus simple de tout mettre en place, serait d'utiliser les [**Générateurs de projet FastAPI**](../project-generation.md){.internal-link target=_blank}.
|
||||
|
||||
Le génerateur de projet adéquat est conçu pour être intégré à ce cluster Docker Swarm avec Traefik et HTTPS décrit ci-dessus.
|
||||
|
||||
Vous pouvez générer un projet en 2 min environ.
|
||||
|
||||
Le projet généré a des instructions pour le déployer et le faire prend 2 min de plus.
|
||||
165
docs/fr/docs/tutorial/body.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Corps de la requête
|
||||
|
||||
Quand vous avez besoin d'envoyer de la donnée depuis un client (comme un navigateur) vers votre API, vous l'envoyez en tant que **corps de requête**.
|
||||
|
||||
Le corps d'une **requête** est de la donnée envoyée par le client à votre API. Le corps d'une **réponse** est la donnée envoyée par votre API au client.
|
||||
|
||||
Votre API aura presque toujours à envoyer un corps de **réponse**. Mais un client n'a pas toujours à envoyer un corps de **requête**.
|
||||
|
||||
Pour déclarer un corps de **requête**, on utilise les modèles de <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> en profitant de tous leurs avantages et fonctionnalités.
|
||||
|
||||
!!! info
|
||||
Pour envoyer de la donnée, vous devriez utiliser : `POST` (le plus populaire), `PUT`, `DELETE` ou `PATCH`.
|
||||
|
||||
Envoyer un corps dans une requête `GET` a un comportement non défini dans les spécifications, cela est néanmoins supporté par **FastAPI**, seulement pour des cas d'utilisation très complexes/extrêmes.
|
||||
|
||||
Ceci étant découragé, la documentation interactive générée par Swagger UI ne montrera pas de documentation pour le corps d'une requête `GET`, et les proxys intermédiaires risquent de ne pas le supporter.
|
||||
|
||||
## Importez le `BaseModel` de Pydantic
|
||||
|
||||
Commencez par importer la classe `BaseModel` du module `pydantic` :
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Créez votre modèle de données
|
||||
|
||||
Déclarez ensuite votre modèle de données en tant que classe qui hérite de `BaseModel`.
|
||||
|
||||
Utilisez les types Python standard pour tous les attributs :
|
||||
|
||||
```Python hl_lines="7-11"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
Tout comme pour la déclaration de paramètres de requête, quand un attribut de modèle a une valeur par défaut, il n'est pas nécessaire. Sinon, cet attribut doit être renseigné dans le corps de la requête. Pour rendre ce champ optionnel simplement, utilisez `None` comme valeur par défaut.
|
||||
|
||||
Par exemple, le modèle ci-dessus déclare un "objet" JSON (ou `dict` Python) tel que :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "An optional description",
|
||||
"price": 45.2,
|
||||
"tax": 3.5
|
||||
}
|
||||
```
|
||||
|
||||
...`description` et `tax` étant des attributs optionnels (avec `None` comme valeur par défaut), cet "objet" JSON serait aussi valide :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 45.2
|
||||
}
|
||||
```
|
||||
|
||||
## Déclarez-le comme paramètre
|
||||
|
||||
Pour l'ajouter à votre *opération de chemin*, déclarez-le comme vous déclareriez des paramètres de chemin ou de requête :
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
...et déclarez que son type est le modèle que vous avez créé : `Item`.
|
||||
|
||||
## Résultats
|
||||
|
||||
En utilisant uniquement les déclarations de type Python, **FastAPI** réussit à :
|
||||
|
||||
* Lire le contenu de la requête en tant que JSON.
|
||||
* Convertir les types correspondants (si nécessaire).
|
||||
* Valider la donnée.
|
||||
* Si la donnée est invalide, une erreur propre et claire sera renvoyée, indiquant exactement où était la donnée incorrecte.
|
||||
* Passer la donnée reçue dans le paramètre `item`.
|
||||
* Ce paramètre ayant été déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support offert par l'éditeur (auto-complétion, etc.) pour tous les attributs de ce paramètre et les types de ces attributs.
|
||||
* Générer des définitions <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> pour votre modèle, qui peuvent être utilisées où vous en avez besoin dans votre projet ensuite.
|
||||
* Ces schémas participeront à la constitution du schéma généré OpenAPI, et seront donc utilisés par les documentations automatiquement générées.
|
||||
|
||||
## Documentation automatique
|
||||
|
||||
Les schémas JSON de vos modèles seront intégrés au schéma OpenAPI global de votre application, et seront donc affichés dans la documentation interactive de l'API :
|
||||
|
||||
<img src="/img/tutorial/body/image01.png">
|
||||
|
||||
Et seront aussi utilisés dans chaque *opération de chemin* de la documentation utilisant ces modèles :
|
||||
|
||||
<img src="/img/tutorial/body/image02.png">
|
||||
|
||||
## Support de l'éditeur
|
||||
|
||||
Dans votre éditeur, vous aurez des annotations de types et de l'auto-complétion partout dans votre fonction (ce qui n'aurait pas été le cas si vous aviez utilisé un classique `dict` plutôt qu'un modèle Pydantic) :
|
||||
|
||||
<img src="/img/tutorial/body/image03.png">
|
||||
|
||||
Et vous obtenez aussi de la vérification d'erreur pour les opérations incorrectes de types :
|
||||
|
||||
<img src="/img/tutorial/body/image04.png">
|
||||
|
||||
Ce n'est pas un hasard, ce framework entier a été bati avec ce design comme objectif.
|
||||
|
||||
Et cela a été rigoureusement testé durant la phase de design, avant toute implémentation, pour s'assurer que cela fonctionnerait avec tous les éditeurs.
|
||||
|
||||
Des changements sur Pydantic ont même été faits pour supporter cela.
|
||||
|
||||
Les captures d'écrans précédentes ont été prises sur <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
|
||||
|
||||
Mais vous auriez le même support de l'éditeur avec <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> et la majorité des autres éditeurs de code Python.
|
||||
|
||||
<img src="/img/tutorial/body/image05.png">
|
||||
|
||||
!!! tip "Astuce"
|
||||
Si vous utilisez <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> comme éditeur, vous pouvez utiliser le Plugin <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>.
|
||||
|
||||
Ce qui améliore le support pour les modèles Pydantic avec :
|
||||
|
||||
* de l'auto-complétion
|
||||
* des vérifications de type
|
||||
* du "refactoring" (ou remaniement de code)
|
||||
* de la recherche
|
||||
* de l'inspection
|
||||
|
||||
## Utilisez le modèle
|
||||
|
||||
Dans la fonction, vous pouvez accéder à tous les attributs de l'objet du modèle directement :
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Corps de la requête + paramètres de chemin
|
||||
|
||||
Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la même *opération de chemin*.
|
||||
|
||||
**FastAPI** est capable de reconnaître que les paramètres de la fonction qui correspondent aux paramètres de chemin doivent être **récupérés depuis le chemin**, et que les paramètres de fonctions déclarés comme modèles Pydantic devraient être **récupérés depuis le corps de la requête**.
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Corps de la requête + paramètres de chemin et de requête
|
||||
|
||||
Vous pouvez aussi déclarer un **corps**, et des paramètres de **chemin** et de **requête** dans la même *opération de chemin*.
|
||||
|
||||
**FastAPI** saura reconnaître chacun d'entre eux et récupérer la bonne donnée au bon endroit.
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
Les paramètres de la fonction seront reconnus comme tel :
|
||||
|
||||
* Si le paramètre est aussi déclaré dans le **chemin**, il sera utilisé comme paramètre de chemin.
|
||||
* Si le paramètre est d'un **type singulier** (comme `int`, `float`, `str`, `bool`, etc.), il sera interprété comme un paramètre de **requête**.
|
||||
* Si le paramètre est déclaré comme ayant pour type un **modèle Pydantic**, il sera interprété comme faisant partie du **corps** de la requête.
|
||||
|
||||
!!! note
|
||||
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `=None`.
|
||||
|
||||
Le type `Optional` dans `Optional[str]` n'est pas utilisé par **FastAPI**, mais sera utile à votre éditeur pour améliorer le support offert par ce dernier et détecter plus facilement des erreurs de type.
|
||||
|
||||
## Sans Pydantic
|
||||
|
||||
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres de **Corps**. Pour cela, allez voir la partie de la documentation sur [Corps de la requête - Paramètres multiples](body-multiple-params.md){.internal-link target=_blank}.
|
||||