Compare commits

...

83 Commits

Author SHA1 Message Date
Sebastián Ramírez
378fa4ef75 🔖 Release version 0.68.2 2021-10-05 15:32:18 +02:00
Sebastián Ramírez
eb515b1af7 📝 Re-structure release notes 2021-10-05 15:31:39 +02:00
github-actions
19fd336cde 📝 Update release notes 2021-10-05 13:02:28 +00:00
Yagiz Degirmenci
63744b2e8a 🌐 Add Turkish translation for docs/features.md (#1950)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 15:01:50 +02:00
github-actions
85f0d2b924 📝 Update release notes 2021-10-05 12:51:14 +00:00
Hüseyin Emre Armağan
507e9baf9e 🌐 Add Turkish translation for docs/benchmarks.md (#2729)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 12:50:38 +00:00
github-actions
c6461ad7dc 📝 Update release notes 2021-10-05 12:46:49 +00:00
Yagiz Degirmenci
5fbf597cd5 🌐 Add Turkish translation for docs/index.md (#1908)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 12:46:11 +00:00
github-actions
bc99d2b7b8 📝 Update release notes 2021-10-05 12:31:22 +00:00
Sam Courtemanche
0eb27ab4d0 🌐 Add French translation for docs/tutorial/body.md (#3671)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 12:30:41 +00:00
github-actions
68c43eb126 📝 Update release notes 2021-10-05 12:28:05 +00:00
Ruidy
00ac07f65c 🌐 Add French translation for deployment/docker.md (#3694)
Co-authored-by: Sam Courtemanche <smlep.pro@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 14:27:24 +02:00
github-actions
d8b0a9dc6e 📝 Update release notes 2021-10-05 12:19:45 +00:00
Felipe Silva
8c36572d23 🌐 Add Portuguese translation for docs/tutorial/path-params.md (#3664)
Co-authored-by: Mário Victor Ribeiro Silva <mariovictorrs@gmail.com>
Co-authored-by: Izabela Guerreiro <izaguerreiro@gmail.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Lucas <61513630+lsglucas@users.noreply.github.com>
Co-authored-by: Felipe <dasilva.delipex93@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 12:19:03 +00:00
jaystone776
e0e3224588 🌐 Delete old Chinese translation for docs/deployment.md (#3840)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 11:03:59 +00:00
github-actions
4e2595f928 📝 Update release notes 2021-10-05 10:40:42 +00:00
Lucas
6de8748788 🌐 Add Portuguese translation for docs/deployment/https.md (#3754) 2021-10-05 12:40:05 +02:00
github-actions
39ec5b33d9 📝 Update release notes 2021-10-05 10:24:47 +00:00
Sebastián Ramírez
557fe61d92 Update GitHub Action: notify-translations, to avoid a race conditon (#3989) 2021-10-05 10:24:02 +00:00
github-actions
458a7fbf15 📝 Update release notes 2021-10-05 10:18:13 +00:00
Sebastián Ramírez
ae22bca9fe ⬆️ Upgrade development autoflake, supporting multi-line imports (#3988) 2021-10-05 10:17:31 +00:00
github-actions
9229ba8078 📝 Update release notes 2021-10-05 10:08:26 +00:00
Sebastián Ramírez
96ca425b1f ⬆️ Increase dependency ranges for tests and docs: pytest-cov, pytest-asyncio, black, httpx, sqlalchemy, databases, mkdocs-markdownextradata-plugin (#3987) 2021-10-05 10:07:40 +00:00
github-actions
c86a4e1dd3 📝 Update release notes 2021-10-05 09:55:12 +00:00
github-actions[bot]
24749aef71 👥 Update FastAPI People (#3986)
Co-authored-by: github-actions <github-actions@github.com>
2021-10-05 11:54:36 +02:00
github-actions
f6a3bc2902 📝 Update release notes 2021-10-05 09:17:56 +00:00
SnkSynthesis
ba8c78d87f ⬆Increase supported version of aiofiles to suppress warnings (#2899)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 11:17:14 +02:00
github-actions
69784e5141 📝 Update release notes 2021-10-05 08:58:41 +00:00
Filipe Laíns
8a02a47124 Do not require backports in Python >= 3.7 (#1880)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 10:58:00 +02:00
github-actions
5607c198c4 📝 Update release notes 2021-10-05 08:47:25 +00:00
Taneli Hukkinen
cf5e67590a ⬆ Upgrade required Python version to >= 3.6.1, needed by typing.Deque, used by Pydantic (#2733)
Co-authored-by: Taneli Hukkinen <hukkinj1@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 10:46:42 +02:00
github-actions
655db2af1f 📝 Update release notes 2021-10-05 08:40:44 +00:00
ArcLight_Slavik
8c102814fd ⬆ Upgrade internal testing dependencies: mypy to version 0.910, add newly needed type packages (#3350)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 10:40:08 +02:00
github-actions
189ac3e280 📝 Update release notes 2021-10-05 08:07:20 +00:00
Marcelo Trylesinski
2680f369d0 ⬆️ Bump Uvicorn max range to 0.15.0 (#3345)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-05 10:06:35 +02:00
github-actions
284eb6615d 📝 Update release notes 2021-10-03 18:14:25 +00:00
Andy Challis
099c478655 💚 Fix badges in README and main page (#3979)
Co-authored-by: Anthony Lukach <anthonylukach@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-10-03 20:13:47 +02:00
github-actions
54c02d402a 📝 Update release notes 2021-10-03 18:01:11 +00:00
Sebastián Ramírez
4b968c4e39 📝 Update GraphQL docs, recommend Strawberry (#3981) 2021-10-03 20:00:28 +02:00
github-actions
1db0fc2953 📝 Update release notes 2021-10-02 10:17:32 +00:00
Sebastián Ramírez
d29aa3d436 Add Deepset Sponsorship (#3976) 2021-10-02 12:16:55 +02:00
github-actions
1b6350ad9e 📝 Update release notes 2021-10-01 20:44:53 +00:00
Sebastián Ramírez
cb7e79ab8a 📝 Re-write and extend Deployment guide: Concepts, Uvicorn, Gunicorn, Docker, Containers, Kubernetes (#3974) 2021-10-01 22:44:19 +02:00
github-actions
fe086a4903 📝 Update release notes 2021-09-27 14:41:20 +00:00
Sebastián Ramírez
64e7deaebc 📝 Upgrade HTTPS guide with more explanations and diagrams (#3950) 2021-09-27 14:40:38 +00:00
github-actions
bee35f5ae1 📝 Update release notes 2021-09-13 17:29:47 +00:00
Maximilian Wassink
3a786a7a3a 🌐 Add German translation for docs/features.md (#3699) 2021-09-13 19:29:03 +02:00
github-actions
eaeafc32c4 📝 Update release notes 2021-09-10 14:29:04 +00:00
Sebastián Ramírez
c8b4e4d455 🎨 Tweak CSS styles for shell animations (#3888) 2021-09-10 14:28:22 +00:00
github-actions
1760da0efa 📝 Update release notes 2021-08-27 08:49:40 +00:00
Sebastián Ramírez
f49ba24b71 🔧 Add new Sponsor Calmcode.io (#3777) 2021-08-27 08:49:08 +00:00
Sebastián Ramírez
7b6e198d31 🔖 Release version 0.68.1 2021-08-24 14:16:09 +02:00
Sebastián Ramírez
6ac35b1c2a 📝 Update release notes 2021-08-24 14:13:47 +02:00
github-actions
dc9c570733 📝 Update release notes 2021-08-24 10:21:45 +00:00
Sebastián Ramírez
20d4834546 Add support for read_with_orm_mode, to support SQLModel relationship attributes (#3757) 2021-08-24 10:21:05 +00:00
github-actions
f7d7c6c865 📝 Update release notes 2021-08-19 08:21:59 +00:00
Mário Victor Ribeiro Silva
c03b9b2d4c 🌐 Add Portuguese translation of docs/fastapi-people.md (#3461)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-08-19 10:21:21 +02:00
github-actions
70f1b8ebd0 📝 Update release notes 2021-08-18 13:50:17 +00:00
jaystone776
13bf477579 🌐 Add Chinese translation for docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md (#3492)
Co-authored-by: Waynerv <ampedee@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-08-18 13:49:44 +00:00
github-actions
ab5afd0b42 📝 Update release notes 2021-08-18 13:44:46 +00:00
Sebastián Ramírez
b5dcd98605 🔧 Add new Translation tracking issues for German and Indonesian (#3718) 2021-08-18 13:44:11 +00:00
github-actions
2280a48c67 📝 Update release notes 2021-08-18 13:32:37 +00:00
jaystone776
8c5d1f30dc 🌐 Add Chinese translation for docs/tutorial/dependencies/sub-dependencies.md (#3491)
Co-authored-by: Waynerv <ampedee@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-08-18 13:31:59 +00:00
github-actions
98a0fe8628 📝 Update release notes 2021-08-18 13:27:46 +00:00
Mário Victor Ribeiro Silva
38c52fa12d 🌐 Add Portuguese translation for docs/advanced/index.md (#3460)
Co-authored-by: Lucas <61513630+lsglucas@users.noreply.github.com>
2021-08-18 15:27:05 +02:00
github-actions
7c2ed25d6f 📝 Update release notes 2021-08-18 13:24:58 +00:00
Fabio Serrao
dda105c50d 🌐 Portuguese translation of docs/async.md (#1330)
Co-authored-by: Lucas <61513630+lsglucas@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-08-18 13:24:21 +00:00
github-actions
326837443f 📝 Update release notes 2021-08-18 13:20:25 +00:00
Sam Courtemanche
cb5b4e1b7e 🌐 Add French translation for docs/async.md (#3416)
Co-authored-by: Jacquet Corentin <corentin.jacquet@telecomnancy.net>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-08-18 15:19:43 +02:00
github-actions
a23eaaeda8 📝 Update release notes 2021-08-18 13:07:50 +00:00
Sebastián Ramírez
d0ced2c7c9 Add GitHub Action: Notify Translations (#3715) 2021-08-18 15:07:08 +02:00
github-actions
3409c9e32f 📝 Update release notes 2021-08-18 09:48:18 +00:00
Sebastián Ramírez
53a86a885d Update computation of FastAPI People and sponsors (#3714) 2021-08-18 09:47:39 +00:00
github-actions
4ac310875d 📝 Update release notes 2021-08-17 20:47:40 +00:00
Sebastián Ramírez
30e742d566 Enable recent Material for MkDocs Insiders features (#3710) 2021-08-17 20:47:05 +00:00
github-actions
84490a2160 📝 Update release notes 2021-08-17 20:01:09 +00:00
Sebastián Ramírez
4d4452ede5 🔥 Remove/clean extra imports from examples in docs for features (#3709) 2021-08-17 20:00:29 +00:00
github-actions
25a48287ed 📝 Update release notes 2021-08-02 14:54:26 +00:00
Sebastián Ramírez
9a33a0e24c Update docs library to include sources in Markdown (#3648) 2021-08-02 14:53:52 +00:00
github-actions
27fd954a45 📝 Update release notes 2021-08-02 14:03:15 +00:00
Marcelo Trylesinski
ab33ba27af ⬆ Add support for Python 3.9 (#2298)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2021-08-02 16:02:34 +02:00
github-actions
e9ba01dc59 📝 Update release notes 2021-08-02 13:42:35 +00:00
github-actions[bot]
e809bdc170 👥 Update FastAPI People (#3642)
Co-authored-by: github-actions <github-actions@github.com>
2021-08-02 15:42:02 +02:00
103 changed files with 7325 additions and 1372 deletions

View File

@@ -0,0 +1,7 @@
FROM python:3.7
RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,<6.0.0"
COPY ./app /app
CMD ["python", "/app/main.py"]

View File

@@ -0,0 +1,10 @@
name: "Notify Translations"
description: "Notify in the issue for a translation when there's a new PR available"
author: "Sebastián Ramírez <tiangolo@gmail.com>"
inputs:
token:
description: 'Token, to read the GitHub API. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
required: true
runs:
using: 'docker'
image: 'Dockerfile'

View File

@@ -0,0 +1,104 @@
import logging
import time
from pathlib import Path
import random
from typing import Dict, Optional
import yaml
from github import Github
from pydantic import BaseModel, BaseSettings, SecretStr
awaiting_label = "awaiting review"
lang_all_label = "lang-all"
approved_label = "approved-2"
translations_path = Path(__file__).parent / "translations.yml"
class Settings(BaseSettings):
github_repository: str
input_token: SecretStr
github_event_path: Path
github_event_name: Optional[str] = None
input_debug: Optional[bool] = False
class PartialGitHubEventIssue(BaseModel):
number: int
class PartialGitHubEvent(BaseModel):
pull_request: PartialGitHubEventIssue
if __name__ == "__main__":
settings = Settings()
if settings.input_debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
logging.debug(f"Using config: {settings.json()}")
g = Github(settings.input_token.get_secret_value())
repo = g.get_repo(settings.github_repository)
if not settings.github_event_path.is_file():
raise RuntimeError(
f"No github event file available at: {settings.github_event_path}"
)
contents = settings.github_event_path.read_text()
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}, 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()])
if lang_all_label in label_strs and awaiting_label in label_strs:
logging.info(
f"This PR seems to be a language translation and awaiting reviews: {pr.number}"
)
if approved_label in label_strs:
message = (
f"It seems this PR already has the approved label: {pr.number}"
)
logging.error(message)
raise RuntimeError(message)
langs = []
for label in label_strs:
if label.startswith("lang-") and not label == lang_all_label:
langs.append(label[5:])
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}"
)
issue = repo.get_issue(num)
message = f"Good news everyone! 😉 There's a new translation PR to be reviewed: #{pr.number} 🎉"
already_notified = False
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}"
)
issue.create_comment(message)
else:
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}"
)
logging.info("Finished")

View File

@@ -0,0 +1,14 @@
pt: 1211
es: 1218
zh: 1228
ru: 1362
it: 1556
ja: 1572
uk: 1748
tr: 1892
fr: 1972
ko: 2017
sq: 2041
pl: 3169
de: 3716
id: 3717

View File

@@ -476,21 +476,16 @@ if __name__ == "__main__":
)
tiers = get_individual_sponsors(settings=settings)
sponsors_50 = []
for login, sponsor in tiers[50].items():
sponsors_50.append(
{"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url}
)
keys = list(tiers.keys())
keys.sort(reverse=True)
sponsors = []
for key in keys:
if key >= 50:
continue
sponsor_group = []
for login, sponsor in tiers[key].items():
sponsors.append(
sponsor_group.append(
{"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url}
)
sponsors.append(sponsor_group)
people = {
"maintainers": maintainers,
@@ -498,16 +493,21 @@ if __name__ == "__main__":
"last_month_active": last_month_active,
"top_contributors": top_contributors,
"top_reviewers": top_reviewers,
"sponsors_50": sponsors_50,
}
github_sponsors = {
"sponsors": sponsors,
}
people_path = Path("./docs/en/data/people.yml")
github_sponsors_path = Path("./docs/en/data/github_sponsors.yml")
people_old_content = people_path.read_text(encoding="utf-8")
new_content = yaml.dump(people, sort_keys=False, width=200, allow_unicode=True)
if people_old_content == new_content:
github_sponsors_old_content = github_sponsors_path.read_text(encoding="utf-8")
new_people_content = yaml.dump(people, sort_keys=False, width=200, allow_unicode=True)
new_github_sponsors_content = yaml.dump(github_sponsors, sort_keys=False, width=200, allow_unicode=True)
if people_old_content == new_people_content and github_sponsors_old_content == new_github_sponsors_content:
logging.info("The FastAPI People data hasn't changed, finishing.")
sys.exit(0)
people_path.write_text(new_content, encoding="utf-8")
people_path.write_text(new_people_content, encoding="utf-8")
github_sponsors_path.write_text(new_github_sponsors_content, encoding="utf-8")
logging.info("Setting up GitHub Actions git user")
subprocess.run(["git", "config", "user.name", "github-actions"], check=True)
subprocess.run(

View File

@@ -0,0 +1,21 @@
name: Notify Translations
on:
pull_request_target:
types:
- labeled
jobs:
notify-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
with:
limit-access-to-actor: true
- uses: ./.github/actions/notify-translations
with:
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -2,6 +2,8 @@ name: Test
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize]
@@ -10,9 +12,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
python-version: [3.6, 3.7, 3.8, 3.9]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Set up Python

View File

@@ -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">
@@ -44,11 +44,13 @@ The key features are:
<!-- sponsors -->
<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://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://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 -->
@@ -314,7 +316,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.
@@ -371,7 +373,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.
@@ -416,9 +418,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**
@@ -445,7 +447,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:

203
docs/de/docs/features.md Normal file
View 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.
![Swagger UI Interaktion](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Alternative API-Dokumentation mit <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### 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>:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
* in <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png)
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.

View File

@@ -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:

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -53,12 +54,13 @@ nav:
- tr: /tr/
- uk: /uk/
- zh: /zh/
- features.md
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +69,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +122,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -0,0 +1,379 @@
sponsors:
- - login: jina-ai
avatarUrl: https://avatars.githubusercontent.com/u/60539444?v=4
url: https://github.com/jina-ai
- - login: mikeckennedy
avatarUrl: https://avatars.githubusercontent.com/u/2035561?v=4
url: https://github.com/mikeckennedy
- login: RodneyU215
avatarUrl: https://avatars.githubusercontent.com/u/3329665?u=ec6a9adf8e7e8e306eed7d49687c398608d1604f&v=4
url: https://github.com/RodneyU215
- login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
- login: deta
avatarUrl: https://avatars.githubusercontent.com/u/47275976?v=4
url: https://github.com/deta
- login: investsuite
avatarUrl: https://avatars.githubusercontent.com/u/73833632?v=4
url: https://github.com/investsuite
- login: vimsoHQ
avatarUrl: https://avatars.githubusercontent.com/u/77627231?v=4
url: https://github.com/vimsoHQ
- - login: newrelic
avatarUrl: https://avatars.githubusercontent.com/u/31739?v=4
url: https://github.com/newrelic
- login: qaas
avatarUrl: https://avatars.githubusercontent.com/u/8503759?u=10a6b4391ad6ab4cf9487ce54e3fcb61322d1efc&v=4
url: https://github.com/qaas
- - login: johnadjei
avatarUrl: https://avatars.githubusercontent.com/u/767860?v=4
url: https://github.com/johnadjei
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
url: https://github.com/wdwinslow
- - login: kamalgill
avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4
url: https://github.com/kamalgill
- login: grillazz
avatarUrl: https://avatars.githubusercontent.com/u/3415861?u=16d7d0ffa5dfb99f8834f8f76d90e138ba09b94a&v=4
url: https://github.com/grillazz
- login: tizz98
avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4
url: https://github.com/tizz98
- login: jmaralc
avatarUrl: https://avatars.githubusercontent.com/u/21101214?u=b15a9f07b7cbf6c9dcdbcb6550bbd2c52f55aa50&v=4
url: https://github.com/jmaralc
- login: AlexandruSimion
avatarUrl: https://avatars.githubusercontent.com/u/71321732?v=4
url: https://github.com/AlexandruSimion
- - login: samuelcolvin
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4
url: https://github.com/samuelcolvin
- login: jokull
avatarUrl: https://avatars.githubusercontent.com/u/701?u=0532b62166893d5160ef795c4c8b7512d971af05&v=4
url: https://github.com/jokull
- login: wshayes
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
- login: koxudaxi
avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4
url: https://github.com/koxudaxi
- login: falkben
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4
url: https://github.com/falkben
- login: jqueguiner
avatarUrl: https://avatars.githubusercontent.com/u/690878?u=e4835b2a985a0f2d52018e4926cb5a58c26a62e8&v=4
url: https://github.com/jqueguiner
- login: Mazyod
avatarUrl: https://avatars.githubusercontent.com/u/860511?v=4
url: https://github.com/Mazyod
- login: ltieman
avatarUrl: https://avatars.githubusercontent.com/u/1084689?u=e69b17de17cb3ca141a17daa7ccbe173ceb1eb17&v=4
url: https://github.com/ltieman
- login: mrmattwright
avatarUrl: https://avatars.githubusercontent.com/u/1277725?v=4
url: https://github.com/mrmattwright
- login: westonsteimel
avatarUrl: https://avatars.githubusercontent.com/u/1593939?u=0f2c0e3647f916fe295d62fa70da7a4c177115e3&v=4
url: https://github.com/westonsteimel
- login: timdrijvers
avatarUrl: https://avatars.githubusercontent.com/u/1694939?v=4
url: https://github.com/timdrijvers
- login: mrgnw
avatarUrl: https://avatars.githubusercontent.com/u/2504532?u=7ec43837a6d0afa80f96f0788744ea6341b89f97&v=4
url: https://github.com/mrgnw
- login: madisonmay
avatarUrl: https://avatars.githubusercontent.com/u/2645393?u=f22b93c6ea345a4d26a90a3834dfc7f0789fcb63&v=4
url: https://github.com/madisonmay
- login: saivarunk
avatarUrl: https://avatars.githubusercontent.com/u/2976867?u=71f4385e781e9a9e871a52f2d4686f9a8d69ba2f&v=4
url: https://github.com/saivarunk
- login: andre1sk
avatarUrl: https://avatars.githubusercontent.com/u/3148093?v=4
url: https://github.com/andre1sk
- login: Shark009
avatarUrl: https://avatars.githubusercontent.com/u/3163309?v=4
url: https://github.com/Shark009
- login: peterHoburg
avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4
url: https://github.com/peterHoburg
- login: jaredtrog
avatarUrl: https://avatars.githubusercontent.com/u/4381365?v=4
url: https://github.com/jaredtrog
- login: CINOAdam
avatarUrl: https://avatars.githubusercontent.com/u/4728508?u=34c3d58cb900fed475d0172b436c66a94ad739ed&v=4
url: https://github.com/CINOAdam
- login: dudil
avatarUrl: https://avatars.githubusercontent.com/u/4785835?u=58b7ea39123e0507f3b2996448a27256b16fd697&v=4
url: https://github.com/dudil
- login: ennui93
avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4
url: https://github.com/ennui93
- login: MacroPower
avatarUrl: https://avatars.githubusercontent.com/u/5648814?u=e13991efd1e03c44c911f919872e750530ded633&v=4
url: https://github.com/MacroPower
- login: ginomempin
avatarUrl: https://avatars.githubusercontent.com/u/6091865?v=4
url: https://github.com/ginomempin
- login: iwpnd
avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=b2286006daafff5f991557344fee20b5da59639a&v=4
url: https://github.com/iwpnd
- login: s3ich4n
avatarUrl: https://avatars.githubusercontent.com/u/6926298?u=ba3025d698e1c986655e776ae383a3d60d9d578e&v=4
url: https://github.com/s3ich4n
- login: Rehket
avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
url: https://github.com/Rehket
- login: christippett
avatarUrl: https://avatars.githubusercontent.com/u/7218120?u=434b9d29287d7de25772d94ddc74a9bd6d969284&v=4
url: https://github.com/christippett
- login: Kludex
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
url: https://github.com/Kludex
- login: Shackelford-Arden
avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4
url: https://github.com/Shackelford-Arden
- login: cristeaadrian
avatarUrl: https://avatars.githubusercontent.com/u/9112724?v=4
url: https://github.com/cristeaadrian
- login: otivvormes
avatarUrl: https://avatars.githubusercontent.com/u/11317418?u=6de1edefb6afd0108c0ad2816bd6efc4464a9c44&v=4
url: https://github.com/otivvormes
- login: iambobmae
avatarUrl: https://avatars.githubusercontent.com/u/12390270?u=c9a35c2ee5092a9b4135ebb1f91b7f521c467031&v=4
url: https://github.com/iambobmae
- login: ronaldnwilliams
avatarUrl: https://avatars.githubusercontent.com/u/13632749?u=ac41a086d0728bf66a9d2bee9e5e377041ff44a4&v=4
url: https://github.com/ronaldnwilliams
- login: pablonnaoji
avatarUrl: https://avatars.githubusercontent.com/u/15187159?u=afc15bd5a4ba9c5c7206bbb1bcaeef606a0932e0&v=4
url: https://github.com/pablonnaoji
- login: natenka
avatarUrl: https://avatars.githubusercontent.com/u/15850513?u=00d1083c980d0b4ce32835dc07eee7f43f34fd2f&v=4
url: https://github.com/natenka
- login: la-mar
avatarUrl: https://avatars.githubusercontent.com/u/16618300?u=7755c0521d2bb0d704f35a51464b15c1e2e6c4da&v=4
url: https://github.com/la-mar
- login: robintully
avatarUrl: https://avatars.githubusercontent.com/u/17059673?u=862b9bb01513f5acd30df97433cb97a24dbfb772&v=4
url: https://github.com/robintully
- login: ShaulAb
avatarUrl: https://avatars.githubusercontent.com/u/18129076?u=2c8d48e47f2dbee15c3f89c3d17d4c356504386c&v=4
url: https://github.com/ShaulAb
- login: wedwardbeck
avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4
url: https://github.com/wedwardbeck
- login: linusg
avatarUrl: https://avatars.githubusercontent.com/u/19366641?u=125e390abef8fff3b3b0d370c369cba5d7fd4c67&v=4
url: https://github.com/linusg
- login: RedCarpetUp
avatarUrl: https://avatars.githubusercontent.com/u/20360440?v=4
url: https://github.com/RedCarpetUp
- login: Filimoa
avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=75e02d102d2ee3e3d793e555fa5c63045913ccb0&v=4
url: https://github.com/Filimoa
- login: raminsj13
avatarUrl: https://avatars.githubusercontent.com/u/24259406?u=d51f2a526312ebba150a06936ed187ca0727d329&v=4
url: https://github.com/raminsj13
- login: comoelcometa
avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=c6751efa038561b9bc5fa56d1033d5174e10cd65&v=4
url: https://github.com/comoelcometa
- login: veprimk
avatarUrl: https://avatars.githubusercontent.com/u/29689749?u=f8cb5a15a286e522e5b189bc572d5a1a90217fb2&v=4
url: https://github.com/veprimk
- login: orihomie
avatarUrl: https://avatars.githubusercontent.com/u/29889683?u=6bc2135a52fcb3a49e69e7d50190796618185fda&v=4
url: https://github.com/orihomie
- login: SaltyCoco
avatarUrl: https://avatars.githubusercontent.com/u/31451104?u=6ee4e17c07d21b7054f54a12fa9cc377a1b24ff9&v=4
url: https://github.com/SaltyCoco
- login: mauroalejandrojm
avatarUrl: https://avatars.githubusercontent.com/u/31569442?u=cdada990a1527926a36e95f62c30a8b48bbc49a1&v=4
url: https://github.com/mauroalejandrojm
- login: bulkw4r3
avatarUrl: https://avatars.githubusercontent.com/u/35562532?u=0b812a14a02de14bf73d05fb2b2760a67bacffc2&v=4
url: https://github.com/bulkw4r3
- login: ybressler
avatarUrl: https://avatars.githubusercontent.com/u/40807730?u=6621dc9ab53b697912ab2a32211bb29ae90a9112&v=4
url: https://github.com/ybressler
- login: dbanty
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4
url: https://github.com/dbanty
- login: dudikbender
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=494f85229115076121b3639a3806bbac1c6ae7f6&v=4
url: https://github.com/dudikbender
- login: primer-io
avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4
url: https://github.com/primer-io
- login: tkrestiankova
avatarUrl: https://avatars.githubusercontent.com/u/67013045?v=4
url: https://github.com/tkrestiankova
- login: daverin
avatarUrl: https://avatars.githubusercontent.com/u/70378377?u=6d1814195c0de7162820eaad95a25b423a3869c0&v=4
url: https://github.com/daverin
- login: anthonycepeda
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4
url: https://github.com/anthonycepeda
- login: an-tho-ny
avatarUrl: https://avatars.githubusercontent.com/u/74874159?v=4
url: https://github.com/an-tho-ny
- - login: linux-china
avatarUrl: https://avatars.githubusercontent.com/u/46711?v=4
url: https://github.com/linux-china
- login: jhb
avatarUrl: https://avatars.githubusercontent.com/u/142217?v=4
url: https://github.com/jhb
- login: yourkin
avatarUrl: https://avatars.githubusercontent.com/u/178984?v=4
url: https://github.com/yourkin
- login: jmagnusson
avatarUrl: https://avatars.githubusercontent.com/u/190835?v=4
url: https://github.com/jmagnusson
- login: sakti
avatarUrl: https://avatars.githubusercontent.com/u/196178?u=0110be74c4270244546f1b610334042cd16bb8ad&v=4
url: https://github.com/sakti
- login: slafs
avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
url: https://github.com/slafs
- login: adamghill
avatarUrl: https://avatars.githubusercontent.com/u/317045?u=f1349d5ffe84a19f324e204777859fbf69ddf633&v=4
url: https://github.com/adamghill
- login: eteq
avatarUrl: https://avatars.githubusercontent.com/u/346587?v=4
url: https://github.com/eteq
- login: dmig
avatarUrl: https://avatars.githubusercontent.com/u/388564?v=4
url: https://github.com/dmig
- login: hongqn
avatarUrl: https://avatars.githubusercontent.com/u/405587?u=470b4c04832e45141fd5264d3354845cc9fc6466&v=4
url: https://github.com/hongqn
- login: rinckd
avatarUrl: https://avatars.githubusercontent.com/u/546002?u=1fcc7e664dc86524a0af6837a0c222829c3fd4e5&v=4
url: https://github.com/rinckd
- login: hardbyte
avatarUrl: https://avatars.githubusercontent.com/u/855189?u=aa29e92f34708814d6b67fcd47ca4cf2ce1c04ed&v=4
url: https://github.com/hardbyte
- login: Pytlicek
avatarUrl: https://avatars.githubusercontent.com/u/1430522?u=169dba3bfbc04ed214a914640ff435969f19ddb3&v=4
url: https://github.com/Pytlicek
- login: okken
avatarUrl: https://avatars.githubusercontent.com/u/1568356?u=0a991a21bdc62e2bea9ad311652f2c45f453dc84&v=4
url: https://github.com/okken
- login: cbonoz
avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4
url: https://github.com/cbonoz
- login: Abbe98
avatarUrl: https://avatars.githubusercontent.com/u/2631719?u=8a064aba9a710229ad28c616549d81a24191a5df&v=4
url: https://github.com/Abbe98
- login: rglsk
avatarUrl: https://avatars.githubusercontent.com/u/2768101?u=e349c88673f2155fe021331377c656a9d74bcc25&v=4
url: https://github.com/rglsk
- login: Atem18
avatarUrl: https://avatars.githubusercontent.com/u/2875254?v=4
url: https://github.com/Atem18
- login: paul121
avatarUrl: https://avatars.githubusercontent.com/u/3116995?u=6e2d8691cc345e63ee02e4eb4d7cef82b1fcbedc&v=4
url: https://github.com/paul121
- login: igorcorrea
avatarUrl: https://avatars.githubusercontent.com/u/3438238?u=c57605077c31a8f7b2341fc4912507f91b4a5621&v=4
url: https://github.com/igorcorrea
- login: anthcor
avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4
url: https://github.com/anthcor
- login: zsinx6
avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4
url: https://github.com/zsinx6
- login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- login: spyker77
avatarUrl: https://avatars.githubusercontent.com/u/4953435?u=03c724c6f8fbab5cd6575b810c0c91c652fa4f79&v=4
url: https://github.com/spyker77
- login: JonasKs
avatarUrl: https://avatars.githubusercontent.com/u/5310116?u=98a049f3e1491bffb91e1feb7e93def6881a9389&v=4
url: https://github.com/JonasKs
- login: holec
avatarUrl: https://avatars.githubusercontent.com/u/6438041?u=f5af71ec85b3a9d7b8139cb5af0512b02fa9ab1e&v=4
url: https://github.com/holec
- login: BartlomiejRasztabiga
avatarUrl: https://avatars.githubusercontent.com/u/8852711?u=ed213d60f7a423df31ceb1004aa3ec60e612cb98&v=4
url: https://github.com/BartlomiejRasztabiga
- login: davanstrien
avatarUrl: https://avatars.githubusercontent.com/u/8995957?u=fb2aad2b52bb4e7b56db6d7c8ecc9ae1eac1b984&v=4
url: https://github.com/davanstrien
- login: and-semakin
avatarUrl: https://avatars.githubusercontent.com/u/9129071?u=ea77ddf7de4bc375d546bf2825ed420eaddb7666&v=4
url: https://github.com/and-semakin
- login: VivianSolide
avatarUrl: https://avatars.githubusercontent.com/u/9358572?u=ffb2e2ec522a15dcd3f0af1f9fd1df4afe418afa&v=4
url: https://github.com/VivianSolide
- login: hard-coders
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
url: https://github.com/hard-coders
- login: satwikkansal
avatarUrl: https://avatars.githubusercontent.com/u/10217535?u=b12d6ef74ea297de9e46da6933b1a5b7ba9e6a61&v=4
url: https://github.com/satwikkansal
- login: pheanex
avatarUrl: https://avatars.githubusercontent.com/u/10408624?u=5b6bab6ee174aa6e991333e06eb29f628741013d&v=4
url: https://github.com/pheanex
- login: wotori
avatarUrl: https://avatars.githubusercontent.com/u/10486621?u=0044c295b91694b8c9bccc0a805681f794250f7b&v=4
url: https://github.com/wotori
- login: JimFawkes
avatarUrl: https://avatars.githubusercontent.com/u/12075115?u=dc58ecfd064d72887c34bf500ddfd52592509acd&v=4
url: https://github.com/JimFawkes
- login: logan-connolly
avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4
url: https://github.com/logan-connolly
- login: iPr0ger
avatarUrl: https://avatars.githubusercontent.com/u/19322290?v=4
url: https://github.com/iPr0ger
- login: ghandic
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: MoronVV
avatarUrl: https://avatars.githubusercontent.com/u/24293616?v=4
url: https://github.com/MoronVV
- login: fstau
avatarUrl: https://avatars.githubusercontent.com/u/24669867?u=60e7c8c09f8dafabee8fc3edcd6f9e19abbff918&v=4
url: https://github.com/fstau
- login: mertguvencli
avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4
url: https://github.com/mertguvencli
- login: rgreen32
avatarUrl: https://avatars.githubusercontent.com/u/35779241?u=c9d64ad1ab364b6a1ec8e3d859da9ca802d681d8&v=4
url: https://github.com/rgreen32
- login: askurihin
avatarUrl: https://avatars.githubusercontent.com/u/37978981?v=4
url: https://github.com/askurihin
- login: JitPackJoyride
avatarUrl: https://avatars.githubusercontent.com/u/40203625?u=9638bfeacfa5940358188f8205ce662bba022b53&v=4
url: https://github.com/JitPackJoyride
- login: es3n1n
avatarUrl: https://avatars.githubusercontent.com/u/40367813?u=e881a3880f1e342d19a1ea7c8e1b6d76c52dc294&v=4
url: https://github.com/es3n1n
- login: ilias-ant
avatarUrl: https://avatars.githubusercontent.com/u/42189572?u=a2d6121bac4d125d92ec207460fa3f1842d37e66&v=4
url: https://github.com/ilias-ant
- login: arrrrrmin
avatarUrl: https://avatars.githubusercontent.com/u/43553423?u=05600727f1cfe75f440bb3fddd49bfea84b1e894&v=4
url: https://github.com/arrrrrmin
- login: akanz1
avatarUrl: https://avatars.githubusercontent.com/u/51492342?u=2280f57134118714645e16b535c1a37adf6b369b&v=4
url: https://github.com/akanz1
- - login: leogregianin
avatarUrl: https://avatars.githubusercontent.com/u/1684053?u=94ddd387601bd1805034dbe83e6eba0491c15323&v=4
url: https://github.com/leogregianin
- login: sadikkuzu
avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=765ed469c44c004560079210ccdad5b29938eaa9&v=4
url: https://github.com/sadikkuzu
- login: gabrielmbmb
avatarUrl: https://avatars.githubusercontent.com/u/29572918?u=92084ed7242160dee4d20aece923a10c59758ee5&v=4
url: https://github.com/gabrielmbmb
- login: starhype
avatarUrl: https://avatars.githubusercontent.com/u/36908028?u=6df41f7b62f0f673f1ecbc87e9cbadaa4fcb0767&v=4
url: https://github.com/starhype
- login: pixel365
avatarUrl: https://avatars.githubusercontent.com/u/53819609?u=9e0309c5420ec4624aececd3ca2d7105f7f68133&v=4
url: https://github.com/pixel365

View File

@@ -1,12 +1,12 @@
maintainers:
- login: tiangolo
answers: 1225
prs: 232
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=05f95ca7fdead36edd9c86be46b4ef6c3c71f876&v=4
answers: 1230
prs: 260
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=5cad72c846b7aba2e960546af490edc7375dafc4&v=4
url: https://github.com/tiangolo
experts:
- login: Kludex
count: 267
count: 299
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
url: https://github.com/Kludex
- login: dmontagu
@@ -14,11 +14,11 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
- login: ycd
count: 216
count: 219
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
url: https://github.com/ycd
- login: Mause
count: 182
count: 207
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: euri10
@@ -30,35 +30,35 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
- login: ArcLightSlavik
count: 64
count: 68
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
url: https://github.com/ArcLightSlavik
- login: falkben
count: 56
count: 57
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4
url: https://github.com/falkben
- login: raphaelauv
count: 47
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- login: sm-Fifteen
count: 46
count: 48
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
- login: raphaelauv
count: 48
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- 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: Dustyposa
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
- login: krishnardt
count: 30
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
url: https://github.com/krishnardt
- login: insomnes
@@ -69,18 +69,26 @@ experts:
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: 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
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
url: https://github.com/chbndrhnns
- login: STeveShary
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
- login: acnebs
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=bfd127b3e6200f4d00afd714f0fc95c2512df19b&v=4
@@ -89,10 +97,6 @@ experts:
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=72adf1cb1d29787305c99700d669561952cea0af&v=4
url: https://github.com/frankie567
- login: chris-allnutt
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
@@ -101,10 +105,18 @@ experts:
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
url: https://github.com/retnikt
- login: ghandic
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: Hultner
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
url: https://github.com/Hultner
- login: panla
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
url: https://github.com/panla
- login: jorgerpo
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4
@@ -113,8 +125,12 @@ experts:
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
url: https://github.com/nkhitrov
- login: adriangb
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4
url: https://github.com/adriangb
- login: waynerv
count: 15
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
- login: haizaar
@@ -122,9 +138,13 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/58201?u=4f1f9843d69433ca0d380d95146cfe119e5fdac4&v=4
url: https://github.com/haizaar
- login: acidjunk
count: 11
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: David-Lor
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/17401854?u=474680c02b94cba810cb9032fb7eb787d9cc9d22&v=4
url: https://github.com/David-Lor
- login: zamiramir
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/40475662?u=e58ef61034e8d0d6a312cc956fb09b9c3332b449&v=4
@@ -133,6 +153,10 @@ experts:
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/8134632?v=4
url: https://github.com/juntatalor
- login: dstlny
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
- login: valentin994
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/42819267?u=fdeeaa9242a59b243f8603496b00994f6951d5a2&v=4
@@ -145,39 +169,47 @@ experts:
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/20441825?u=ee1e59446b98f8ec2363caeda4c17164d0d9cc7d&v=4
url: https://github.com/stefanondisponibile
- login: hellocoldworld
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/47581948?v=4
url: https://github.com/hellocoldworld
last_month_active:
- login: Kludex
count: 12
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
url: https://github.com/Kludex
- login: ghandic
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: STeveShary
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
- login: Honda-a
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/22759187?u=f45bd5fb17b4dca331529b8e9e5eab6122b84b8b&v=4
url: https://github.com/Honda-a
- login: frankie567
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=72adf1cb1d29787305c99700d669561952cea0af&v=4
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
url: https://github.com/frankie567
- login: Mause
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: ArcLightSlavik
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
url: https://github.com/ArcLightSlavik
- login: gyKa
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1000842?v=4
url: https://github.com/gyKa
- login: ricky-sb
- login: panla
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/10700079?v=4
url: https://github.com/ricky-sb
- login: captainCapitalism
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
url: https://github.com/panla
- login: dstlny
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/32553875?v=4
url: https://github.com/captainCapitalism
- login: acidjunk
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
- login: trevorwang
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
avatarUrl: https://avatars.githubusercontent.com/u/121966?v=4
url: https://github.com/trevorwang
- login: klaa97
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/39653693?v=4
url: https://github.com/klaa97
top_contributors:
- login: waynerv
count: 25
@@ -196,17 +228,25 @@ top_contributors:
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
- login: mariacamilagl
count: 9
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
url: https://github.com/mariacamilagl
- login: jaystone776
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
url: https://github.com/jaystone776
- 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
- login: Kludex
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
url: https://github.com/Serrones
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
url: https://github.com/Kludex
- login: hard-coders
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
@@ -223,57 +263,61 @@ top_contributors:
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
url: https://github.com/Attsun1031
- login: jekirl
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2546697?v=4
url: https://github.com/jekirl
- login: komtaki
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=5a44657c0544111ee3c132d9bb9951c2804f7969&v=4
url: https://github.com/komtaki
- login: Smlep
count: 4
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
- login: jekirl
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
url: https://github.com/jekirl
- login: jfunez
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
url: https://github.com/jfunez
- login: komtaki
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
url: https://github.com/komtaki
top_reviewers:
- login: Kludex
count: 80
count: 90
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
url: https://github.com/Kludex
- login: tokusumi
count: 44
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
- login: Laineyzhang55
count: 42
avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
url: https://github.com/Laineyzhang55
- login: ycd
count: 41
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
url: https://github.com/ycd
- login: waynerv
count: 38
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
url: https://github.com/Laineyzhang55
- login: tokusumi
count: 46
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
- login: ycd
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
url: https://github.com/ycd
- login: AdrianDeAnda
count: 28
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=bb7f8a0d6c9de4e9d0320a9f271210206e202250&v=4
url: https://github.com/AdrianDeAnda
- login: ArcLightSlavik
count: 27
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: komtaki
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=5a44657c0544111ee3c132d9bb9951c2804f7969&v=4
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: 16
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4
url: https://github.com/cassiobotaro
- login: yanever
@@ -284,6 +328,10 @@ top_reviewers:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
url: https://github.com/SwftAlpc
- login: hard-coders
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
url: https://github.com/hard-coders
- login: pedabraham
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
@@ -292,16 +340,24 @@ top_reviewers:
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
url: https://github.com/delhi09
- login: Smlep
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
- login: lsglucas
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
url: https://github.com/RunningIkkyu
- login: hard-coders
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
url: https://github.com/hard-coders
- login: sh0nk
count: 11
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
url: https://github.com/sh0nk
- login: mariacamilagl
@@ -324,14 +380,6 @@ top_reviewers:
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
url: https://github.com/bezaca
- login: lsglucas
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
url: https://github.com/lsglucas
- login: rjNemo
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: blt232018
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4
@@ -348,22 +396,34 @@ top_reviewers:
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- login: graingert
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/413772?v=4
url: https://github.com/graingert
- login: NastasiaSaby
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/8245071?u=b3afd005f9e4bf080c219ef61a592b3a8004b764&v=4
url: https://github.com/NastasiaSaby
- login: Smlep
- login: Mause
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: solomein-sv
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=46acfb4aeefb1d7b9fdc5a8cbd9eb8744683c47a&v=4
url: https://github.com/solomein-sv
- login: ComicShrimp
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=b3e4d9a14d9a65d429ce62c566aef73178b7111d&v=4
url: https://github.com/ComicShrimp
- login: jovicon
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4
url: https://github.com/jovicon
- login: Mause
- login: BilalAlpaslan
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
- login: nimctl
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=e39b11d47188744ee07b2a1c7ce1a1bdf3c80760&v=4
@@ -380,6 +440,10 @@ top_reviewers:
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4
url: https://github.com/anthonycepeda
- 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
@@ -388,365 +452,27 @@ top_reviewers:
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/23391143?u=56ab6bff50be950fa8cae5cf736f2ae66e319ff3&v=4
url: https://github.com/rkbeatss
- login: izaguerreiro
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
url: https://github.com/izaguerreiro
- 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: dukkee
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/36825394?u=ccfd86e6a4f2d093dad6f7544cc875af67fa2df8&v=4
url: https://github.com/dukkee
- login: Bluenix2
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/38372706?u=c9d28aff15958d6ebf1971148bfb3154ff943c4f&v=4
url: https://github.com/Bluenix2
sponsors_50:
- login: johnadjei
avatarUrl: https://avatars.githubusercontent.com/u/767860?v=4
url: https://github.com/johnadjei
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
url: https://github.com/wdwinslow
- login: bingwu-chime
avatarUrl: https://avatars.githubusercontent.com/u/67026650?u=603a6b345f25c20c6706a8a6c7f71ae688d649a5&v=4
url: https://github.com/bingwu-chime
sponsors:
- login: kamalgill
avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4
url: https://github.com/kamalgill
- login: grillazz
avatarUrl: https://avatars.githubusercontent.com/u/3415861?u=16d7d0ffa5dfb99f8834f8f76d90e138ba09b94a&v=4
url: https://github.com/grillazz
- login: tizz98
avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4
url: https://github.com/tizz98
- login: jmaralc
avatarUrl: https://avatars.githubusercontent.com/u/21101214?u=b15a9f07b7cbf6c9dcdbcb6550bbd2c52f55aa50&v=4
url: https://github.com/jmaralc
- login: psgandalf
avatarUrl: https://avatars.githubusercontent.com/u/8134158?v=4
url: https://github.com/psgandalf
- login: samuelcolvin
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4
url: https://github.com/samuelcolvin
- login: jokull
avatarUrl: https://avatars.githubusercontent.com/u/701?u=0532b62166893d5160ef795c4c8b7512d971af05&v=4
url: https://github.com/jokull
- login: wshayes
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
- login: koxudaxi
avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4
url: https://github.com/koxudaxi
- login: falkben
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4
url: https://github.com/falkben
- login: jqueguiner
avatarUrl: https://avatars.githubusercontent.com/u/690878?u=e4835b2a985a0f2d52018e4926cb5a58c26a62e8&v=4
url: https://github.com/jqueguiner
- login: Mazyod
avatarUrl: https://avatars.githubusercontent.com/u/860511?v=4
url: https://github.com/Mazyod
- login: ltieman
avatarUrl: https://avatars.githubusercontent.com/u/1084689?u=e69b17de17cb3ca141a17daa7ccbe173ceb1eb17&v=4
url: https://github.com/ltieman
- login: mrmattwright
avatarUrl: https://avatars.githubusercontent.com/u/1277725?v=4
url: https://github.com/mrmattwright
- login: westonsteimel
avatarUrl: https://avatars.githubusercontent.com/u/1593939?u=0f2c0e3647f916fe295d62fa70da7a4c177115e3&v=4
url: https://github.com/westonsteimel
- login: timdrijvers
avatarUrl: https://avatars.githubusercontent.com/u/1694939?v=4
url: https://github.com/timdrijvers
- login: mrgnw
avatarUrl: https://avatars.githubusercontent.com/u/2504532?u=7ec43837a6d0afa80f96f0788744ea6341b89f97&v=4
url: https://github.com/mrgnw
- login: madisonmay
avatarUrl: https://avatars.githubusercontent.com/u/2645393?u=f22b93c6ea345a4d26a90a3834dfc7f0789fcb63&v=4
url: https://github.com/madisonmay
- login: saivarunk
avatarUrl: https://avatars.githubusercontent.com/u/2976867?u=71f4385e781e9a9e871a52f2d4686f9a8d69ba2f&v=4
url: https://github.com/saivarunk
- login: andre1sk
avatarUrl: https://avatars.githubusercontent.com/u/3148093?v=4
url: https://github.com/andre1sk
- login: Shark009
avatarUrl: https://avatars.githubusercontent.com/u/3163309?v=4
url: https://github.com/Shark009
- login: peterHoburg
avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4
url: https://github.com/peterHoburg
- login: dudil
avatarUrl: https://avatars.githubusercontent.com/u/4785835?u=58b7ea39123e0507f3b2996448a27256b16fd697&v=4
url: https://github.com/dudil
- login: ennui93
avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4
url: https://github.com/ennui93
- login: sco1
avatarUrl: https://avatars.githubusercontent.com/u/5323929?u=2b8434060d0c9d93de80a2a945baed94a412c31e&v=4
url: https://github.com/sco1
- login: MacroPower
avatarUrl: https://avatars.githubusercontent.com/u/5648814?u=b2730000c9f9a471282b9849d2cc85711d7973d4&v=4
url: https://github.com/MacroPower
- login: ginomempin
avatarUrl: https://avatars.githubusercontent.com/u/6091865?v=4
url: https://github.com/ginomempin
- login: iwpnd
avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=b2286006daafff5f991557344fee20b5da59639a&v=4
url: https://github.com/iwpnd
- login: s3ich4n
avatarUrl: https://avatars.githubusercontent.com/u/6926298?u=ba3025d698e1c986655e776ae383a3d60d9d578e&v=4
url: https://github.com/s3ich4n
- login: Rehket
avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
url: https://github.com/Rehket
- login: christippett
avatarUrl: https://avatars.githubusercontent.com/u/7218120?u=434b9d29287d7de25772d94ddc74a9bd6d969284&v=4
url: https://github.com/christippett
- login: Kludex
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4
url: https://github.com/Kludex
- login: Shackelford-Arden
avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4
url: https://github.com/Shackelford-Arden
- login: macleodmac
avatarUrl: https://avatars.githubusercontent.com/u/8996312?u=e39c68c3e0b1d264dcba4850134a291680f46355&v=4
url: https://github.com/macleodmac
- login: cristeaadrian
avatarUrl: https://avatars.githubusercontent.com/u/9112724?v=4
url: https://github.com/cristeaadrian
- login: otivvormes
avatarUrl: https://avatars.githubusercontent.com/u/11317418?u=6de1edefb6afd0108c0ad2816bd6efc4464a9c44&v=4
url: https://github.com/otivvormes
- login: iambobmae
avatarUrl: https://avatars.githubusercontent.com/u/12390270?u=c9a35c2ee5092a9b4135ebb1f91b7f521c467031&v=4
url: https://github.com/iambobmae
- login: ronaldnwilliams
avatarUrl: https://avatars.githubusercontent.com/u/13632749?u=ac41a086d0728bf66a9d2bee9e5e377041ff44a4&v=4
url: https://github.com/ronaldnwilliams
- login: uselessscat
avatarUrl: https://avatars.githubusercontent.com/u/15332878?u=8485a1b7383c274b28f383370ee2d5f9a6cd423b&v=4
url: https://github.com/uselessscat
- login: natenka
avatarUrl: https://avatars.githubusercontent.com/u/15850513?u=00d1083c980d0b4ce32835dc07eee7f43f34fd2f&v=4
url: https://github.com/natenka
- login: la-mar
avatarUrl: https://avatars.githubusercontent.com/u/16618300?u=7755c0521d2bb0d704f35a51464b15c1e2e6c4da&v=4
url: https://github.com/la-mar
- login: robintully
avatarUrl: https://avatars.githubusercontent.com/u/17059673?u=862b9bb01513f5acd30df97433cb97a24dbfb772&v=4
url: https://github.com/robintully
- login: ShaulAb
avatarUrl: https://avatars.githubusercontent.com/u/18129076?u=2c8d48e47f2dbee15c3f89c3d17d4c356504386c&v=4
url: https://github.com/ShaulAb
- login: wedwardbeck
avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4
url: https://github.com/wedwardbeck
- login: linusg
avatarUrl: https://avatars.githubusercontent.com/u/19366641?u=125e390abef8fff3b3b0d370c369cba5d7fd4c67&v=4
url: https://github.com/linusg
- login: RedCarpetUp
avatarUrl: https://avatars.githubusercontent.com/u/20360440?v=4
url: https://github.com/RedCarpetUp
- login: daddycocoaman
avatarUrl: https://avatars.githubusercontent.com/u/21189155?u=756f6a17c71c538b11470f70839baacab43807ef&v=4
url: https://github.com/daddycocoaman
- login: Filimoa
avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=75e02d102d2ee3e3d793e555fa5c63045913ccb0&v=4
url: https://github.com/Filimoa
- login: raminsj13
avatarUrl: https://avatars.githubusercontent.com/u/24259406?u=d51f2a526312ebba150a06936ed187ca0727d329&v=4
url: https://github.com/raminsj13
- login: comoelcometa
avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=c6751efa038561b9bc5fa56d1033d5174e10cd65&v=4
url: https://github.com/comoelcometa
- login: veprimk
avatarUrl: https://avatars.githubusercontent.com/u/29689749?u=f8cb5a15a286e522e5b189bc572d5a1a90217fb2&v=4
url: https://github.com/veprimk
- login: orihomie
avatarUrl: https://avatars.githubusercontent.com/u/29889683?u=6bc2135a52fcb3a49e69e7d50190796618185fda&v=4
url: https://github.com/orihomie
- login: SaltyCoco
avatarUrl: https://avatars.githubusercontent.com/u/31451104?u=6ee4e17c07d21b7054f54a12fa9cc377a1b24ff9&v=4
url: https://github.com/SaltyCoco
- login: mauroalejandrojm
avatarUrl: https://avatars.githubusercontent.com/u/31569442?u=cdada990a1527926a36e95f62c30a8b48bbc49a1&v=4
url: https://github.com/mauroalejandrojm
- login: public-daniel
avatarUrl: https://avatars.githubusercontent.com/u/32238294?u=0377e38dd023395c9643d5388b4e9489a24b4d34&v=4
url: https://github.com/public-daniel
- login: ybressler
avatarUrl: https://avatars.githubusercontent.com/u/40807730?u=6621dc9ab53b697912ab2a32211bb29ae90a9112&v=4
url: https://github.com/ybressler
- login: dbanty
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4
url: https://github.com/dbanty
- login: dudikbender
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=494f85229115076121b3639a3806bbac1c6ae7f6&v=4
url: https://github.com/dudikbender
- login: primer-io
avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4
url: https://github.com/primer-io
- login: tkrestiankova
avatarUrl: https://avatars.githubusercontent.com/u/67013045?v=4
url: https://github.com/tkrestiankova
- login: daverin
avatarUrl: https://avatars.githubusercontent.com/u/70378377?u=6d1814195c0de7162820eaad95a25b423a3869c0&v=4
url: https://github.com/daverin
- login: anthonycepeda
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4
url: https://github.com/anthonycepeda
- login: linux-china
avatarUrl: https://avatars.githubusercontent.com/u/46711?v=4
url: https://github.com/linux-china
- login: jhb
avatarUrl: https://avatars.githubusercontent.com/u/142217?v=4
url: https://github.com/jhb
- login: yourkin
avatarUrl: https://avatars.githubusercontent.com/u/178984?v=4
url: https://github.com/yourkin
- login: jmagnusson
avatarUrl: https://avatars.githubusercontent.com/u/190835?v=4
url: https://github.com/jmagnusson
- login: slafs
avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
url: https://github.com/slafs
- login: adamghill
avatarUrl: https://avatars.githubusercontent.com/u/317045?u=f1349d5ffe84a19f324e204777859fbf69ddf633&v=4
url: https://github.com/adamghill
- login: eteq
avatarUrl: https://avatars.githubusercontent.com/u/346587?v=4
url: https://github.com/eteq
- login: dmig
avatarUrl: https://avatars.githubusercontent.com/u/388564?v=4
url: https://github.com/dmig
- login: hongqn
avatarUrl: https://avatars.githubusercontent.com/u/405587?u=470b4c04832e45141fd5264d3354845cc9fc6466&v=4
url: https://github.com/hongqn
- login: rinckd
avatarUrl: https://avatars.githubusercontent.com/u/546002?u=1fcc7e664dc86524a0af6837a0c222829c3fd4e5&v=4
url: https://github.com/rinckd
- login: Pytlicek
avatarUrl: https://avatars.githubusercontent.com/u/1430522?u=169dba3bfbc04ed214a914640ff435969f19ddb3&v=4
url: https://github.com/Pytlicek
- login: okken
avatarUrl: https://avatars.githubusercontent.com/u/1568356?u=0a991a21bdc62e2bea9ad311652f2c45f453dc84&v=4
url: https://github.com/okken
- login: leogregianin
avatarUrl: https://avatars.githubusercontent.com/u/1684053?u=94ddd387601bd1805034dbe83e6eba0491c15323&v=4
url: https://github.com/leogregianin
- login: cbonoz
avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4
url: https://github.com/cbonoz
- login: rglsk
avatarUrl: https://avatars.githubusercontent.com/u/2768101?u=e349c88673f2155fe021331377c656a9d74bcc25&v=4
url: https://github.com/rglsk
- login: Atem18
avatarUrl: https://avatars.githubusercontent.com/u/2875254?v=4
url: https://github.com/Atem18
- login: paul121
avatarUrl: https://avatars.githubusercontent.com/u/3116995?u=6e2d8691cc345e63ee02e4eb4d7cef82b1fcbedc&v=4
url: https://github.com/paul121
- login: igorcorrea
avatarUrl: https://avatars.githubusercontent.com/u/3438238?u=c57605077c31a8f7b2341fc4912507f91b4a5621&v=4
url: https://github.com/igorcorrea
- login: zsinx6
avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4
url: https://github.com/zsinx6
- login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- login: spyker77
avatarUrl: https://avatars.githubusercontent.com/u/4953435?u=568baae6469628e020fe0bab16e395b7ae10c7d3&v=4
url: https://github.com/spyker77
- login: serfer2
avatarUrl: https://avatars.githubusercontent.com/u/5196592?u=e8798d87120952ed41876778f0cc8a1ddb47f901&v=4
url: https://github.com/serfer2
- login: JonasKs
avatarUrl: https://avatars.githubusercontent.com/u/5310116?u=98a049f3e1491bffb91e1feb7e93def6881a9389&v=4
url: https://github.com/JonasKs
- login: holec
avatarUrl: https://avatars.githubusercontent.com/u/6438041?u=f5af71ec85b3a9d7b8139cb5af0512b02fa9ab1e&v=4
url: https://github.com/holec
- login: BartlomiejRasztabiga
avatarUrl: https://avatars.githubusercontent.com/u/8852711?u=ed213d60f7a423df31ceb1004aa3ec60e612cb98&v=4
url: https://github.com/BartlomiejRasztabiga
- login: davanstrien
avatarUrl: https://avatars.githubusercontent.com/u/8995957?u=fb2aad2b52bb4e7b56db6d7c8ecc9ae1eac1b984&v=4
url: https://github.com/davanstrien
- login: and-semakin
avatarUrl: https://avatars.githubusercontent.com/u/9129071?u=ea77ddf7de4bc375d546bf2825ed420eaddb7666&v=4
url: https://github.com/and-semakin
- login: VivianSolide
avatarUrl: https://avatars.githubusercontent.com/u/9358572?u=ffb2e2ec522a15dcd3f0af1f9fd1df4afe418afa&v=4
url: https://github.com/VivianSolide
- login: hard-coders
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4
url: https://github.com/hard-coders
- login: satwikkansal
avatarUrl: https://avatars.githubusercontent.com/u/10217535?u=b12d6ef74ea297de9e46da6933b1a5b7ba9e6a61&v=4
url: https://github.com/satwikkansal
- login: pheanex
avatarUrl: https://avatars.githubusercontent.com/u/10408624?u=5b6bab6ee174aa6e991333e06eb29f628741013d&v=4
url: https://github.com/pheanex
- login: wotori
avatarUrl: https://avatars.githubusercontent.com/u/10486621?u=0044c295b91694b8c9bccc0a805681f794250f7b&v=4
url: https://github.com/wotori
- login: JimFawkes
avatarUrl: https://avatars.githubusercontent.com/u/12075115?u=dc58ecfd064d72887c34bf500ddfd52592509acd&v=4
url: https://github.com/JimFawkes
- login: logan-connolly
avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4
url: https://github.com/logan-connolly
- login: iPr0ger
avatarUrl: https://avatars.githubusercontent.com/u/19322290?v=4
url: https://github.com/iPr0ger
- login: sadikkuzu
avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=765ed469c44c004560079210ccdad5b29938eaa9&v=4
url: https://github.com/sadikkuzu
- login: ghandic
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: MoronVV
avatarUrl: https://avatars.githubusercontent.com/u/24293616?v=4
url: https://github.com/MoronVV
- login: AngusWG
avatarUrl: https://avatars.githubusercontent.com/u/26385612?u=f4d4c8bd2097cdd58eb9e385932b83c78777f3c0&v=4
url: https://github.com/AngusWG
- login: mertguvencli
avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4
url: https://github.com/mertguvencli
- login: rgreen32
avatarUrl: https://avatars.githubusercontent.com/u/35779241?u=c9d64ad1ab364b6a1ec8e3d859da9ca802d681d8&v=4
url: https://github.com/rgreen32
- login: askurihin
avatarUrl: https://avatars.githubusercontent.com/u/37978981?v=4
url: https://github.com/askurihin
- login: berrysauce
avatarUrl: https://avatars.githubusercontent.com/u/38889179?u=758ed15a5be8bbd03855f5a74f42c19f7946ee32&v=4
url: https://github.com/berrysauce
- login: JitPackJoyride
avatarUrl: https://avatars.githubusercontent.com/u/40203625?u=9638bfeacfa5940358188f8205ce662bba022b53&v=4
url: https://github.com/JitPackJoyride
- login: es3n1n
avatarUrl: https://avatars.githubusercontent.com/u/40367813?u=e881a3880f1e342d19a1ea7c8e1b6d76c52dc294&v=4
url: https://github.com/es3n1n
- login: ilias-ant
avatarUrl: https://avatars.githubusercontent.com/u/42189572?u=a2d6121bac4d125d92ec207460fa3f1842d37e66&v=4
url: https://github.com/ilias-ant
- login: kbhatiya999
avatarUrl: https://avatars.githubusercontent.com/u/47816034?v=4
url: https://github.com/kbhatiya999
- login: akanz1
avatarUrl: https://avatars.githubusercontent.com/u/51492342?u=2280f57134118714645e16b535c1a37adf6b369b&v=4
url: https://github.com/akanz1
- login: rychardvale
avatarUrl: https://avatars.githubusercontent.com/u/54805553?u=3d20ab05301d05f9ac3500fb79a2bfee3842b753&v=4
url: https://github.com/rychardvale
- login: athemeart
avatarUrl: https://avatars.githubusercontent.com/u/61623624?v=4
url: https://github.com/athemeart
- login: Rhythmicc
avatarUrl: https://avatars.githubusercontent.com/u/29839231?u=2100781089a259707c475c4547bd7995b0fc18ee&v=4
url: https://github.com/Rhythmicc

View File

@@ -1,11 +1,11 @@
gold:
- url: https://www.deta.sh/?ref=fastapi
title: The launchpad for all your (team's) ideas
img: https://fastapi.tiangolo.com/img/sponsors/deta.svg
- 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
silver:
- url: https://www.deta.sh/?ref=fastapi
title: The launchpad for all your (team's) ideas
img: https://fastapi.tiangolo.com/img/sponsors/deta.svg
- url: https://www.investsuite.com/jobs
title: Wealthtech jobs with FastAPI
img: https://fastapi.tiangolo.com/img/sponsors/investsuite.svg
@@ -15,7 +15,13 @@ silver:
- url: https://talkpython.fm/fastapi-sponsor
title: FastAPI video courses on demand from people you trust
img: https://fastapi.tiangolo.com/img/sponsors/talkpython.png
bronze:
- 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

View File

@@ -0,0 +1,8 @@
logins:
- jina-ai
- deta
- investsuite
- vimsoHQ
- mikeckennedy
- koaning
- deepset-ai

View File

@@ -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.

View File

@@ -367,7 +367,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 +374,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.

View File

@@ -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.

View File

@@ -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;
@@ -20,6 +34,7 @@ a.internal-link::after {
.user-list {
display: flex;
flex-wrap: wrap;
margin-bottom: 2rem;
}
.user-list-center {

View File

@@ -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;

View 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 for 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 in 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 on 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, its own variables, its own 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 or 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 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, maybe 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. 🚀

View File

@@ -238,3 +238,21 @@ You can also edit them and re-play them.
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 a lot 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.

View File

@@ -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>. And then you can deploy that container image in one of different 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>
## 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 in 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 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 in 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 this 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
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## 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:
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 other 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 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. 🤓

View File

@@ -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. 🔒

View File

@@ -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 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. ✨

View File

@@ -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"
@@ -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,24 @@ 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.
## Deployment Concepts
But if you are doing all that, you might just use the Docker image that does it automatically.
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. 🚀

View 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. ✨

View File

@@ -114,54 +114,55 @@ These are the **Sponsors**. 😎
They are supporting my work with **FastAPI** (and others), mainly through <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>.
{% if sponsors %}
{% if sponsors.gold %}
### Gold Sponsors
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.silver %}
### Silver Sponsors
{% if sponsors %}
{% for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### Bronze Sponsors
{% if sponsors %}
{% for sponsor in sponsors.bronze -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% endif %}
### Individual Sponsors
{% if people %}
{% if people.sponsors_50 %}
{% if github_sponsors %}
{% for group in github_sponsors.sponsors %}
<div class="user-list user-list-center">
{% for user in people.sponsors_50 %}
{% for user in group %}
{% if user.login not in sponsors_badge.logins %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if people %}
<div class="user-list user-list-center">
{% for user in people.sponsors %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endfor %}
</div>
{% endif %}
## About the data - technical details

View File

@@ -32,7 +32,6 @@ If you need a 2 minute refresher of how to use Python types (even if you don't u
You write standard Python with types:
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel
@@ -165,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`.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server&lt;/span&gt;&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;RAM&lt;br&gt;&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;CPU&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Process&amp;nbsp;&lt;/span&gt;&lt;/font&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Manager&lt;/span&gt;" 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="&lt;font face=&quot;roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Worker Process&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Worker Process&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Another Process&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;1 GB&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;1 GB&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;Cert Renovation Program&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Let's Encrypt&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;FastAPI&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt; app for: someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;Another app&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;: another.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;One more app&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;: onemore.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px ; font-weight: 400&quot;&gt;A Database&lt;/span&gt;&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Plain response from: someapp.example.com&lt;/span&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Renew HTTPS cert for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;New HTTPS cert for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted response from: someapp.example.com&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Decrypted request for: someapp.example.com&lt;/span&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

View 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;FastAPI&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt; app for: someapp.example.com&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Decrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;FastAPI&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt; app for: someapp.example.com&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Plain response from: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Decrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;FastAPI&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt; app for: someapp.example.com&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Plain response from: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Decrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted response from: someapp.example.com&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

View 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;Server(s)&lt;/span&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;https://someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;DNS Servers&lt;/font&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;TLS Termination Proxy&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;FastAPI&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt; app for: someapp.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;Another app&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;: another.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;One more app&lt;/font&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px ; font-weight: normal&quot;&gt;: onemore.example.com&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px ; font-weight: 400&quot;&gt;A Database&lt;/span&gt;&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Plain response from: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Decrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 24px&quot;&gt;Port 443 (HTTPS)&lt;/font&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted request for: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Who is: someapp.example.com&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br style=&quot;font-family: &amp;#34;roboto&amp;#34;&quot;&gt;&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;TLS Handshake&lt;/span&gt;" 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="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 24px&quot;&gt;Encrypted response from: someapp.example.com&lt;/span&gt;" 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="&lt;font style=&quot;font-size: 24px&quot; face=&quot;Roboto&quot;&gt;HTTPS certificates&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;someapp.example.com&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;another.example.net&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;onemore.example.org&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot;&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;IP:&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-size: 24px&quot;&gt;123.124.125.126&lt;/span&gt;&lt;br&gt;&lt;/font&gt;" 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>

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 33 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View 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

View File

@@ -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">
@@ -317,7 +317,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 +374,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 +419,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**
@@ -448,7 +448,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:

View File

@@ -2,6 +2,73 @@
## Latest Changes
## 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).
### Translations
* 🌐 Add Portuguese translation of `docs/fastapi-people.md`. PR [#3461](https://github.com/tiangolo/fastapi/pull/3461) by [@ComicShrimp](https://github.com/ComicShrimp).
* 🌐 Add Chinese translation for `docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md`. PR [#3492](https://github.com/tiangolo/fastapi/pull/3492) by [@jaystone776](https://github.com/jaystone776).
* 🔧 Add new Translation tracking issues for German and Indonesian. PR [#3718](https://github.com/tiangolo/fastapi/pull/3718) by [@tiangolo](https://github.com/tiangolo).
* 🌐 Add Chinese translation for `docs/tutorial/dependencies/sub-dependencies.md`. PR [#3491](https://github.com/tiangolo/fastapi/pull/3491) by [@jaystone776](https://github.com/jaystone776).
* 🌐 Add Portuguese translation for `docs/advanced/index.md`. PR [#3460](https://github.com/tiangolo/fastapi/pull/3460) by [@ComicShrimp](https://github.com/ComicShrimp).
* 🌐 Portuguese translation of `docs/async.md`. PR [#1330](https://github.com/tiangolo/fastapi/pull/1330) by [@Serrones](https://github.com/Serrones).
* 🌐 Add French translation for `docs/async.md`. PR [#3416](https://github.com/tiangolo/fastapi/pull/3416) by [@Smlep](https://github.com/Smlep).
### Internal
* ✨ Add GitHub Action: Notify Translations. PR [#3715](https://github.com/tiangolo/fastapi/pull/3715) by [@tiangolo](https://github.com/tiangolo).
* ✨ Update computation of FastAPI People and sponsors. PR [#3714](https://github.com/tiangolo/fastapi/pull/3714) by [@tiangolo](https://github.com/tiangolo).
* ✨ Enable recent Material for MkDocs Insiders features. PR [#3710](https://github.com/tiangolo/fastapi/pull/3710) by [@tiangolo](https://github.com/tiangolo).
* 🔥 Remove/clean extra imports from examples in docs for features. PR [#3709](https://github.com/tiangolo/fastapi/pull/3709) by [@tiangolo](https://github.com/tiangolo).
* Update docs library to include sources in Markdown. PR [#3648](https://github.com/tiangolo/fastapi/pull/3648) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Enable tests for Python 3.9. PR [#2298](https://github.com/tiangolo/fastapi/pull/2298) by [@Kludex](https://github.com/Kludex).
* 👥 Update FastAPI People. PR [#3642](https://github.com/tiangolo/fastapi/pull/3642) by [@github-actions[bot]](https://github.com/apps/github-actions).
## 0.68.0

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: img/icon-white.svg
@@ -146,9 +147,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
@@ -162,7 +165,10 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
# Uncomment these 2 lines during development to more easily add highlights
# - pymdownx.highlight:
# linenums: true
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -171,7 +177,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -224,6 +230,5 @@ extra_css:
- css/termynal.css
- css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- js/termynal.js
- js/custom.js

View File

@@ -22,12 +22,12 @@
</div>
</div>
<div id="announce-right" style="position: relative;">
<div class="item">
<!-- <div class="item">
<a title="The launchpad for all your (team's) ideas" style="display: block; position: relative;" href="https://www.deta.sh/?ref=fastapi" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/deta-banner.svg" />
</a>
</div>
</div> -->
<div class="item">
<a title="Get three courses at 10% off their current prices! Plus, we'll be donating 10% of all profits from sales of this bundle to the FastAPI team." style="display: block; position: relative;" href="https://testdriven.io/talkpython/" target="_blank">
<span class="sponsor-badge">sponsor</span>

View File

@@ -32,7 +32,6 @@ Si necesitas un repaso de 2 minutos de cómo usar los tipos de Python (así no u
Escribes Python estándar con tipos así:
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -68,7 +69,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -77,7 +78,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -130,6 +131,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

394
docs/fr/docs/async.md Normal file
View File

@@ -0,0 +1,394 @@
# Concurrence et les mots-clés async et await
Cette page vise à fournir des détails sur la syntaxe `async def` pour les *fonctions de chemins* et quelques rappels sur le code asynchrone, la concurrence et le parallélisme.
## Vous êtes pressés ?
<abbr title="'too long; didn't read' en anglais, ou 'trop long ; j'ai pas lu'"><strong>TL;DR :</strong></abbr>
Si vous utilisez des bibliothèques tierces qui nécessitent d'être appelées avec `await`, telles que :
```Python
results = await some_library()
```
Alors, déclarez vos *fonctions de chemins* avec `async def` comme ceci :
```Python hl_lines="2"
@app.get('/')
async def read_results():
results = await some_library()
return results
```
!!! note
Vous pouvez uniquement utiliser `await` dans les fonctions créées avec `async def`.
---
Si vous utilisez une bibliothèque externe qui communique avec quelque chose (une BDD, une API, un système de fichiers, etc.) et qui ne supporte pas l'utilisation d'`await` (ce qui est actuellement le cas pour la majorité des bibliothèques de BDD), alors déclarez vos *fonctions de chemin* normalement, avec le classique `def`, comme ceci :
```Python hl_lines="2"
@app.get('/')
def results():
results = some_library()
return results
```
---
Si votre application n'a pas à communiquer avec une bibliothèque externe et pas à attendre de réponse, utilisez `async def`.
---
Si vous ne savez pas, utilisez seulement `def` comme vous le feriez habituellement.
---
**Note** : vous pouvez mélanger `def` et `async def` dans vos *fonctions de chemin* autant que nécessaire, **FastAPI** saura faire ce qu'il faut avec.
Au final, peu importe le cas parmi ceux ci-dessus, **FastAPI** fonctionnera de manière asynchrone et sera extrêmement rapide.
Mais si vous suivez bien les instructions ci-dessus, alors **FastAPI** pourra effectuer quelques optimisations et ainsi améliorer les performances.
## Détails techniques
Les versions modernes de Python supportent le **code asynchrone** grâce aux **"coroutines"** avec les syntaxes **`async` et `await`**.
Analysons les différentes parties de cette phrase dans les sections suivantes :
* **Code asynchrone**
* **`async` et `await`**
* **Coroutines**
## Code asynchrone
Faire du code asynchrone signifie que le langage 💬 est capable de dire à l'ordinateur / au programme 🤖 qu'à un moment du code, il 🤖 devra attendre que *quelque chose d'autre* se termine autre part. Disons que ce *quelque chose d'autre* est appelé "fichier-lent" 📝.
Donc, pendant ce temps, l'ordinateur pourra effectuer d'autres tâches, pendant que "fichier-lent" 📝 se termine.
Ensuite l'ordinateur / le programme 🤖 reviendra à chaque fois qu'il en a la chance que ce soit parce qu'il attend à nouveau, ou car il 🤖 a fini tout le travail qu'il avait à faire. Il 🤖 regardera donc si les tâches qu'il attend ont terminé d'être effectuées.
Ensuite, il 🤖 prendra la première tâche à finir (disons, notre "fichier-lent" 📝) et continuera à faire avec cette dernière ce qu'il était censé.
Ce "attendre quelque chose d'autre" fait généralement référence à des opérations <abbr title="Input/Output ou Entrées et Sorties ">I/O</abbr> qui sont relativement "lentes" (comparées à la vitesse du processeur et de la mémoire RAM) telles qu'attendre que :
* de la donnée soit envoyée par le client à travers le réseau
* de la donnée envoyée depuis votre programme soit reçue par le client à travers le réseau
* le contenu d'un fichier sur le disque soit lu par le système et passé à votre programme
* le contenu que votre programme a passé au système soit écrit sur le disque
* une opération effectuée à distance par une API se termine
* une opération en BDD se termine
* une requête à une BDD renvoie un résultat
* etc.
Le temps d'exécution étant consommé majoritairement par l'attente d'opérations <abbr title="Input/Output ou Entrées et Sorties ">I/O</abbr> on appelle ceci des opérations <a href="https://fr.wikipedia.org/wiki/I/O_bound" class="external-link" target="_blank">"I/O bound"</a>.
Ce concept se nomme l'"asynchronisme" car l'ordinateur / le programme n'a pas besoin d'être "synchronisé" avec la tâche, attendant le moment exact où cette dernière se terminera en ne faisant rien, pour être capable de récupérer le résultat de la tâche et l'utiliser dans la suite des opérations.
À la place, en étant "asynchrone", une fois terminée, une tâche peut légèrement attendre (quelques microsecondes) que l'ordinateur / le programme finisse ce qu'il était en train de faire, et revienne récupérer le résultat.
Pour parler de tâches "synchrones" (en opposition à "asynchrones"), on utilise souvent le terme "séquentiel", car l'ordinateur / le programme va effectuer toutes les étapes d'une tâche séquentiellement avant de passer à une autre tâche, même si ces étapes impliquent de l'attente.
### Concurrence et Burgers
L'idée de code **asynchrone** décrite ci-dessus est parfois aussi appelée **"concurrence"**. Ce qui est différent du **"parallélisme"**.
La **concurrence** et le **parallélisme** sont tous deux liés à l'idée de "différentes choses arrivant plus ou moins au même moment".
Mais les détails entre la **concurrence** et le **parallélisme** diffèrent sur de nombreux points.
Pour expliquer la différence, voici une histoire de burgers :
#### Burgers concurrents
Vous amenez votre crush 😍 dans votre fast food 🍔 favori, et faites la queue pendant que le serveur 💁 prend les commandes des personnes devant vous.
Puis vient votre tour, vous commandez alors 2 magnifiques burgers 🍔 pour votre crush 😍 et vous.
Vous payez 💸.
Le serveur 💁 dit quelque chose à son collègue dans la cuisine 👨‍🍳 pour qu'il sache qu'il doit préparer vos burgers 🍔 (bien qu'il soit déjà en train de préparer ceux des clients précédents).
Le serveur 💁 vous donne le numéro assigné à votre commande.
Pendant que vous attendez, vous allez choisir une table avec votre crush 😍, vous discutez avec votre crush 😍 pendant un long moment (les burgers étant "magnifiques" ils sont très longs à préparer ✨🍔✨).
Pendant que vous êtes assis à table, en attendant que les burgers 🍔 soient prêts, vous pouvez passer ce temps à admirer à quel point votre crush 😍 est géniale, mignonne et intelligente ✨😍✨.
Pendant que vous discutez avec votre crush 😍, de temps en temps vous jetez un coup d'oeil au nombre affiché au-dessus du comptoir pour savoir si c'est à votre tour d'être servis.
Jusqu'au moment où c'est (enfin) votre tour. Vous allez au comptoir, récupérez vos burgers 🍔 et revenez à votre table.
Vous et votre crush 😍 mangez les burgers 🍔 et passez un bon moment ✨.
---
Imaginez que vous êtes l'ordinateur / le programme 🤖 dans cette histoire.
Pendant que vous faites la queue, vous être simplement inactif 😴, attendant votre tour, ne faisant rien de "productif". Mais la queue est rapide car le serveur 💁 prend seulement les commandes (et ne les prépare pas), donc tout va bien.
Ensuite, quand c'est votre tour, vous faites des actions "productives" 🤓, vous étudiez le menu, décidez ce que vous voulez, demandez à votre crush 😍 son choix, payez 💸, vérifiez que vous utilisez la bonne carte de crédit, vérifiez que le montant débité sur la carte est correct, vérifiez que la commande contient les bons produits, etc.
Mais ensuite, même si vous n'avez pas encore vos burgers 🍔, votre travail avec le serveur 💁 est "en pause" ⏸, car vous devez attendre 🕙 que vos burgers soient prêts.
Après vous être écarté du comptoir et vous être assis à votre table avec le numéro de votre commande, vous pouvez tourner 🔀 votre attention vers votre crush 😍, et "travailler" ⏯ 🤓 là-dessus. Vous êtes donc à nouveau en train de faire quelque chose de "productif" 🤓, vous flirtez avec votre crush 😍.
Puis le serveur 💁 dit "J'ai fini de préparer les burgers" 🍔 en mettant votre numéro sur l'affichage du comptoir, mais vous ne courrez pas immédiatement au moment où votre numéro s'affiche. Vous savez que personne ne volera vos burgers 🍔 car vous avez votre numéro et les autres clients ont le leur.
Vous attendez donc que votre crush 😍 finisse son histoire, souriez gentiment et dites que vous allez chercher les burgers ⏸.
Pour finir vous allez au comptoir 🔀, vers la tâche initiale qui est désormais terminée ⏯, récupérez les burgers 🍔, remerciez le serveur et ramenez les burgers 🍔 à votre table. Ceci termine l'étape / la tâche d'interaction avec le comptoir ⏹. Ce qui ensuite, crée une nouvelle tâche de "manger les burgers" 🔀 ⏯, mais la précédente, "récupérer les burgers" est terminée ⏹.
#### Burgers parallèles
Imaginons désormais que ce ne sont pas des "burgers concurrents" mais des "burgers parallèles".
Vous allez avec votre crush 😍 dans un fast food 🍔 parallélisé.
Vous attendez pendant que plusieurs (disons 8) serveurs qui sont aussi des cuisiniers 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳 prennent les commandes des personnes devant vous.
Chaque personne devant vous attend 🕙 que son burger 🍔 soit prêt avant de quitter le comptoir car chacun des 8 serveurs va lui-même préparer le burger directement avant de prendre la commande suivante.
Puis c'est enfin votre tour, vous commandez 2 magnifiques burgers 🍔 pour vous et votre crush 😍.
Vous payez 💸.
Le serveur va dans la cuisine 👨‍🍳.
Vous attendez devant le comptoir afin que personne ne prenne vos burgers 🍔 avant vous, vu qu'il n'y a pas de numéro de commande.
Vous et votre crush 😍 étant occupés à vérifier que personne ne passe devant vous prendre vos burgers au moment où ils arriveront 🕙, vous ne pouvez pas vous préoccuper de votre crush 😞.
C'est du travail "synchrone", vous être "synchronisés" avec le serveur/cuisinier 👨‍🍳. Vous devez attendre 🕙 et être présent au moment exact où le serveur/cuisinier 👨‍🍳 finira les burgers 🍔 et vous les donnera, sinon quelqu'un risque de vous les prendre.
Puis le serveur/cuisinier 👨‍🍳 revient enfin avec vos burgers 🍔, après un long moment d'attente 🕙 devant le comptoir.
Vous prenez vos burgers 🍔 et allez à une table avec votre crush 😍
Vous les mangez, et vous avez terminé 🍔 ⏹.
Durant tout ce processus, il n'y a presque pas eu de discussions ou de flirts car la plupart de votre temps à été passé à attendre 🕙 devant le comptoir 😞.
---
Dans ce scénario de burgers parallèles, vous êtes un ordinateur / programme 🤖 avec deux processeurs (vous et votre crush 😍) attendant 🕙 à deux et dédiant votre attention 🕙 à "attendre devant le comptoir" pour une longue durée.
Le fast-food a 8 processeurs (serveurs/cuisiniers) 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳. Alors que le fast-food de burgers concurrents en avait 2 (un serveur et un cuisinier).
Et pourtant l'expérience finale n'est pas meilleure 😞.
---
C'est donc l'histoire équivalente parallèle pour les burgers 🍔.
Pour un exemple plus courant dans la "vie réelle", imaginez une banque.
Jusqu'à récemment, la plupart des banques avaient plusieurs caisses (et banquiers) 👨‍💼👨‍💼👨‍💼👨‍💼 et une unique file d'attente 🕙🕙🕙🕙🕙🕙🕙🕙.
Tous les banquiers faisaient l'intégralité du travail avec chaque client avant de passer au suivant 👨‍💼⏯.
Et vous deviez attendre 🕙 dans la file pendant un long moment ou vous perdiez votre place.
Vous n'auriez donc probablement pas envie d'amener votre crush 😍 avec vous à la banque 🏦.
#### Conclusion
Dans ce scénario des "burgers du fast-food avec votre crush", comme il y a beaucoup d'attente 🕙, il est très logique d'avoir un système concurrent ⏸🔀⏯.
Et c'est le cas pour la plupart des applications web.
Vous aurez de nombreux, nombreux utilisateurs, mais votre serveur attendra 🕙 que leur connexion peu performante envoie des requêtes.
Puis vous attendrez 🕙 de nouveau que leurs réponses reviennent.
Cette "attente" 🕙 se mesure en microsecondes, mais tout de même, en cumulé cela fait beaucoup d'attente.
C'est pourquoi il est logique d'utiliser du code asynchrone ⏸🔀⏯ pour des APIs web.
La plupart des frameworks Python existants (y compris Flask et Django) ont été créés avant que les nouvelles fonctionnalités asynchrones de Python n'existent. Donc, les façons dont ils peuvent être déployés supportent l'exécution parallèle et une ancienne forme d'exécution asynchrone qui n'est pas aussi puissante que les nouvelles fonctionnalités de Python.
Et cela, bien que les spécifications principales du web asynchrone en Python (ou ASGI) ont été développées chez Django, pour ajouter le support des WebSockets.
Ce type d'asynchronicité est ce qui a rendu NodeJS populaire (bien que NodeJS ne soit pas parallèle) et c'est la force du Go en tant que langage de programmation.
Et c'est le même niveau de performance que celui obtenu avec **FastAPI**.
Et comme on peut avoir du parallélisme et de l'asynchronicité en même temps, on obtient des performances plus hautes que la plupart des frameworks NodeJS et égales à celles du Go, qui est un langage compilé plus proche du C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(tout ça grâce à Starlette)</a>.
### Est-ce que la concurrence est mieux que le parallélisme ?
Nope ! C'est ça la morale de l'histoire.
La concurrence est différente du parallélisme. C'est mieux sur des scénarios **spécifiques** qui impliquent beaucoup d'attente. À cause de ça, c'est généralement bien meilleur que le parallélisme pour le développement d'applications web. Mais pas pour tout.
Donc pour équilibrer tout ça, imaginez l'histoire suivante :
> Vous devez nettoyer une grande et sale maison.
*Oui, c'est toute l'histoire*.
---
Il n'y a plus d'attente 🕙 nulle part, juste beaucoup de travail à effectuer, dans différentes pièces de la maison.
Vous pourriez diviser en différentes sections comme avec les burgers, d'abord le salon, puis la cuisine, etc. Mais vous n'attendez 🕙 rien, vous ne faites que nettoyer et nettoyer, la séparation en sections ne changerait rien au final.
Cela prendrait autant de temps pour finir avec ou sans sections (concurrence) et vous auriez effectué la même quantité de travail.
Mais dans ce cas, si pouviez amener 8 ex-serveurs/cuisiniers/devenus-nettoyeurs 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳, et que chacun d'eux (plus vous) pouvait prendre une zone de la maison pour la nettoyer, vous pourriez faire tout le travail en parallèle, et finir plus tôt.
Dans ce scénario, chacun des nettoyeurs (vous y compris) serait un processeur, faisant sa partie du travail.
Et comme la plupart du temps d'exécution est pris par du "vrai" travail (et non de l'attente), et que le travail dans un ordinateur est fait par un <abbr title="Central Processing Unit">CPU</abbr>, ce sont des problèmes dits "CPU bound".
---
Des exemples communs d'opérations "CPU bounds" sont les procédés qui requièrent des traitements mathématiques complexes.
Par exemple :
* Traitements d'**audio** et d'**images**.
* La **vision par ordinateur** : une image est composée de millions de pixels, chaque pixel ayant 3 valeurs / couleurs, les traiter tous va nécessiter d'effectuer des traitements sur chaque pixel, et de préférence tous en même temps.
* L'apprentissage automatique (ou **Machine Learning**) : cela nécessite de nombreuses multiplications de matrices et vecteurs. Imaginez une énorme feuille de calcul remplie de nombres que vous multiplierez entre eux tous au même moment.
* L'apprentissage profond (ou **Deep Learning**) : est un sous-domaine du **Machine Learning**, donc les mêmes raisons s'appliquent. Avec la différence qu'il n'y a pas une unique feuille de calcul de nombres à multiplier, mais une énorme quantité d'entre elles, et dans de nombreux cas, on utilise un processeur spécial pour construire et / ou utiliser ces modèles.
### Concurrence + Parallélisme : Web + Machine Learning
Avec **FastAPI** vous pouvez bénéficier de la concurrence qui est très courante en developement web (c'est l'attrait principal de NodeJS).
Mais vous pouvez aussi profiter du parallélisme et multiprocessing afin de gérer des charges **CPU bound** qui sont récurrentes dans les systèmes de *Machine Learning*.
Ça, ajouté au fait que Python soit le langage le plus populaire pour la **Data Science**, le **Machine Learning** et surtout le **Deep Learning**, font de **FastAPI** un très bon choix pour les APIs et applications de **Data Science** / **Machine Learning**.
Pour comprendre comment mettre en place ce parallélisme en production, allez lire la section [Déploiement](deployment/index.md){.internal-link target=_blank}.
## `async` et `await`
Les versions modernes de Python ont une manière très intuitive de définir le code asynchrone, tout en gardant une apparence de code "séquentiel" classique en laissant Python faire l'attente pour vous au bon moment.
Pour une opération qui nécessite de l'attente avant de donner un résultat et qui supporte ces nouvelles fonctionnalités Python, vous pouvez l'utiliser comme tel :
```Python
burgers = await get_burgers(2)
```
Le mot-clé important ici est `await`. Il informe Python qu'il faut attendre ⏸ que `get_burgers(2)` finisse d'effectuer ses opérations 🕙 avant de stocker les résultats dans la variable `burgers`. Grâce à cela, Python saura qu'il peut aller effectuer d'autres opérations 🔀 ⏯ pendant ce temps (comme par exemple recevoir une autre requête).
Pour que `await` fonctionne, il doit être placé dans une fonction qui supporte l'asynchronicité. Pour que ça soit le cas, il faut déclarer cette dernière avec `async def` :
```Python hl_lines="1"
async def get_burgers(number: int):
# Opérations asynchrones pour créer les burgers
return burgers
```
...et non `def` :
```Python hl_lines="2"
# Ceci n'est pas asynchrone
def get_sequential_burgers(number: int):
# Opérations asynchrones pour créer les burgers
return burgers
```
Avec `async def`, Python sait que dans cette fonction il doit prendre en compte les expressions `await`, et qu'il peut mettre en pause ⏸ l'exécution de la fonction pour aller faire autre chose 🔀 avant de revenir.
Pour appeler une fonction définie avec `async def`, vous devez utiliser `await`. Donc ceci ne marche pas :
```Python
# Ceci ne fonctionne pas, car get_burgers a été défini avec async def
burgers = get_burgers(2)
```
---
Donc, si vous utilisez une bibliothèque qui nécessite que ses fonctions soient appelées avec `await`, vous devez définir la *fonction de chemin* en utilisant `async def` comme dans :
```Python hl_lines="2-3"
@app.get('/burgers')
async def read_burgers():
burgers = await get_burgers(2)
return burgers
```
### Plus de détails techniques
Vous avez donc compris que `await` peut seulement être utilisé dans des fonctions définies avec `async def`.
Mais en même temps, les fonctions définies avec `async def` doivent être appelées avec `await` et donc dans des fonctions définies elles aussi avec `async def`.
Vous avez donc remarqué ce paradoxe d'oeuf et de la poule, comment appelle-t-on la première fonction `async` ?
Si vous utilisez **FastAPI**, pas besoin de vous en inquiéter, car cette "première" fonction sera votre *fonction de chemin* ; et **FastAPI** saura comment arriver au résultat attendu.
Mais si vous utilisez `async` / `await` sans **FastAPI**, <a href="https://docs.python.org/3/library/asyncio-task.html#coroutine" class="external-link" target="_blank">allez jetez un coup d'oeil à la documentation officielle de Python</a>.
### Autres formes de code asynchrone
L'utilisation d'`async` et `await` est relativement nouvelle dans ce langage.
Mais cela rend la programmation asynchrone bien plus simple.
Cette même syntaxe (ou presque) était aussi incluse dans les versions modernes de Javascript (dans les versions navigateur et NodeJS).
Mais avant ça, gérer du code asynchrone était bien plus complexe et difficile.
Dans les versions précédentes de Python, vous auriez utilisé des *threads* ou <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Mais le code aurait été bien plus difficile à comprendre, débugger, et concevoir.
Dans les versions précédentes de Javascript NodeJS / Navigateur, vous auriez utilisé des "callbacks". Menant potentiellement à ce que l'on appelle <a href="http://callbackhell.com/" class="external-link" target="_blank">le "callback hell"</a>.
## Coroutines
**Coroutine** est juste un terme élaboré pour désigner ce qui est retourné par une fonction définie avec `async def`. Python sait que c'est comme une fonction classique qui va démarrer à un moment et terminer à un autre, mais qu'elle peut aussi être mise en pause ⏸, du moment qu'il y a un `await` dans son contenu.
Mais toutes ces fonctionnalités d'utilisation de code asynchrone avec `async` et `await` sont souvent résumées comme l'utilisation des *coroutines*. On peut comparer cela à la principale fonctionnalité clé de Go, les "Goroutines".
## Conclusion
Reprenons la phrase du début de la page :
> Les versions modernes de Python supportent le **code asynchrone** grâce aux **"coroutines"** avec les syntaxes **`async` et `await`**.
Ceci devrait être plus compréhensible désormais. ✨
Tout ceci est donc ce qui donne sa force à **FastAPI** (à travers Starlette) et lui permet d'avoir des performances aussi impressionnantes.
## Détails très techniques
!!! warning "Attention !"
Vous pouvez probablement ignorer cela.
Ce sont des détails très poussés sur comment **FastAPI** fonctionne en arrière-plan.
Si vous avez de bonnes connaissances techniques (coroutines, threads, code bloquant, etc.) et êtes curieux de comment **FastAPI** gère `async def` versus le `def` classique, cette partie est faite pour vous.
### Fonctions de chemin
Quand vous déclarez une *fonction de chemin* avec un `def` normal et non `async def`, elle est exécutée dans un groupe de threads (threadpool) externe qui est ensuite attendu, plutôt que d'être appelée directement (car cela bloquerait le serveur).
Si vous venez d'un autre framework asynchrone qui ne fonctionne pas comme de la façon décrite ci-dessus et que vous êtes habitués à définir des *fonctions de chemin* basiques avec un simple `def` pour un faible gain de performance (environ 100 nanosecondes), veuillez noter que dans **FastAPI**, l'effet serait plutôt contraire. Dans ces cas-là, il vaut mieux utiliser `async def` à moins que votre *fonction de chemin* utilise du code qui effectue des opérations <abbr title="Input/Output ou Entrées et Sorties ">I/O</abbr> bloquantes.
Au final, dans les deux situations, il est fort probable que **FastAPI** soit tout de même [plus rapide](/#performance){.internal-link target=_blank} que (ou au moins de vitesse égale à) votre framework précédent.
### Dépendances
La même chose s'applique aux dépendances. Si une dépendance est définie avec `def` plutôt que `async def`, elle est exécutée dans la threadpool externe.
### Sous-dépendances
Vous pouvez avoir de multiples dépendances et sous-dépendances dépendant les unes des autres (en tant que paramètres de la définition de la *fonction de chemin*), certaines créées avec `async def` et d'autres avec `def`. Cela fonctionnerait aussi, et celles définies avec un simple `def` seraient exécutées sur un thread externe (venant de la threadpool) plutôt que d'être "attendues".
### Autres fonctions utilitaires
Toute autre fonction utilitaire que vous appelez directement peut être créée avec un classique `def` ou avec `async def` et **FastAPI** n'aura pas d'impact sur la façon dont vous l'appelez.
Contrairement aux fonctions que **FastAPI** appelle pour vous : les *fonctions de chemin* et dépendances.
Si votre fonction utilitaire est une fonction classique définie avec `def`, elle sera appelée directement (telle qu'écrite dans votre code), pas dans une threadpool, si la fonction est définie avec `async def` alors vous devrez attendre (avec `await`) que cette fonction se termine avant de passer à la suite du code.
---
Encore une fois, ce sont des détails très techniques qui peuvent être utiles si vous venez ici les chercher.
Sinon, les instructions de la section <a href="#vous-etes-presses">Vous êtes pressés ?</a> ci-dessus sont largement suffisantes.

View 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>) :
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
## 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>) :
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## 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.

View File

@@ -114,25 +114,28 @@ Ce sont les **Sponsors**. 😎
Ils soutiennent mon travail avec **FastAPI** (et d'autres) avec <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>.
{% if sponsors.gold %}
### Gold Sponsors
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.silver %}
### Silver Sponsors
{% if sponsors %}
{% for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### Bronze Sponsors
{% if sponsors %}
{% for sponsor in sponsors.bronze -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
@@ -140,28 +143,22 @@ Ils soutiennent mon travail avec **FastAPI** (et d'autres) avec <a href="https:/
### Individual Sponsors
{% if people %}
{% if people.sponsors_50 %}
{% if github_sponsors %}
{% for group in github_sponsors.sponsors %}
<div class="user-list user-list-center">
{% for user in people.sponsors_50 %}
{% for user in group %}
{% if user.login not in sponsors_badge.logins %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if people %}
<div class="user-list user-list-center">
{% for user in people.sponsors %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endfor %}
</div>
{% endif %}
## À propos des données - détails techniques

View File

@@ -32,7 +32,6 @@ Si vous souhaitez un rappel de 2 minutes sur l'utilisation des types en Python (
Vous écrivez du python standard avec des annotations de types:
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel

View 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}.

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -57,7 +58,11 @@ nav:
- fastapi-people.md
- python-types.md
- Tutoriel - Guide utilisateur:
- tutorial/body.md
- tutorial/background-tasks.md
- async.md
- Déploiement:
- deployment/docker.md
- project-generation.md
- alternatives.md
- external-links.md
@@ -66,7 +71,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -75,7 +80,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -128,6 +133,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -58,7 +59,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +68,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +121,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -58,7 +59,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +68,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +121,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -115,25 +115,28 @@ FastAPIには、様々なバックグラウンドの人々を歓迎する素晴
彼らは、<a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a> を介して私の **FastAPI** などに関する活動を支援してくれています。
{% if sponsors.gold %}
### Gold Sponsors
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.silver %}
### Silver Sponsors
{% if sponsors %}
{% for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### Bronze Sponsors
{% if sponsors %}
{% for sponsor in sponsors.bronze -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
@@ -141,28 +144,22 @@ FastAPIには、様々なバックグラウンドの人々を歓迎する素晴
### Individual Sponsors
{% if people %}
{% if people.sponsors_50 %}
{% if github_sponsors %}
{% for group in github_sponsors.sponsors %}
<div class="user-list user-list-center">
{% for user in people.sponsors_50 %}
{% for user in group %}
{% if user.login not in sponsors_badge.logins %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if people %}
<div class="user-list user-list-center">
{% for user in people.sponsors %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endfor %}
</div>
{% endif %}
## データについて - 技術詳細

View File

@@ -31,7 +31,6 @@ FastAPIの機能はすべて、標準のPython 3.6型宣言に基づいていま
型を使用した標準的なPythonを記述します:
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -98,7 +99,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -107,7 +108,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -160,6 +161,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -64,7 +65,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -73,7 +74,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -126,6 +127,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -58,7 +59,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +68,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +121,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -0,0 +1,24 @@
# Guia de Usuário Avançado - Introdução
## Recursos Adicionais
O [Tutorial - Guia de Usuário](../tutorial/){.internal-link target=_blank} deve ser o suficiente para dar a você um tour por todos os principais recursos do **FastAPI**.
Na próxima seção você verá outras opções, configurações, e recursos adicionais.
!!! tip "Dica"
As próximas seções **não são necessáriamente "avançadas"**
E é possível que para seu caso de uso, a solução esteja em uma delas.
## Leia o Tutorial primeiro
Você ainda pode usar a maior parte dos recursos no **FastAPI** com o conhecimento do [Tutorial - Guia de Usuário](../tutorial/){.internal-link target=_blank}.
E as próximas seções assumem que você já leu ele, e que você conhece suas ideias principais.
## Curso TestDriven.io
Se você gostaria de fazer um curso avançado-iniciante para complementar essa seção da documentação, você pode querer conferir: <a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">Test-Driven Development com FastAPI e Docker</a> por **TestDriven.io**.
Eles estão atualmente doando 10% de todos os lucros para o desenvolvimento do **FastAPI**. 🎉 😄

394
docs/pt/docs/async.md Normal file
View File

@@ -0,0 +1,394 @@
# Concorrência e async / await
Detalhes sobre a sintaxe `async def` para *funções de operação de rota* e alguns conceitos de código assíncrono, concorrência e paralelismo.
## Com pressa?
<abbr title="muito longo; não li"><strong>TL;DR:</strong></abbr>
Se você estiver utilizando bibliotecas de terceiros que dizem para você chamar as funções com `await`, como:
```Python
results = await some_library()
```
Então, declare sua *função de operação de rota* com `async def` como:
```Python hl_lines="2"
@app.get('/')
async def read_results():
results = await some_library()
return results
```
!!! note
Você só pode usar `await` dentro de funções criadas com `async def`.
---
Se você está usando biblioteca de terceiros que se comunica com alguma coisa (um banco de dados, uma API, sistema de arquivos etc) e não tem suporte para utilizar `await` (esse é atualmente o caso para a maioria das bibliotecas de banco de dados), então declare suas *funções de operação de rota* normalmente, com apenas `def`, como:
```Python hl_lines="2"
@app.get('/')
def results():
results = some_library()
return results
```
---
Se sua aplicação (de alguma forma) não tem que se comunicar com nada mais e tem que esperar que o respondam, use `async def`.
---
Se você simplesmente não sabe, use apenas `def`.
---
**Note**: Você pode misturar `def` e `async def` nas suas *funções de operação de rota* tanto quanto necessário e definir cada função usando a melhor opção para você. FastAPI irá fazer a coisa certa com elas.
De qualquer forma, em ambos os casos acima, FastAPI irá trabalhar assincronamente e ser extremamente rápido.
Seguindo os passos acima, ele será capaz de fazer algumas otimizações de performance.
## Detalhes Técnicos
Versões modernas de Python tem suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**.
Vamos ver aquela frase por partes na seção abaixo:
* **Código assíncrono**
* **`async` e `await`**
* **Corrotinas**
## Código assíncrono
Código assíncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa 🤖 que em certo ponto, ele 🤖 terá que esperar por *algo* para finalizar em outro lugar. Vamos dizer que esse *algo* seja chamado "arquivo lento" 📝.
Então, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termine.
Então o computador / programa 🤖 irá voltar toda hora que tiver uma chance porquê ele ainda está esperando o "arquivo lento", ou ele 🤖 nunca irá terminar todo o trabalho que tem até esse ponto. E ele 🤖 irá ver se alguma das tarefas que estava esperando já terminaram, fazendo o que quer que tinham que fazer.
Depois, ele 🤖 pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que ele tem que fazer com isso.
Esse "esperar por algo" normalmente se refere a operações <abbr title="Entrada e Saída">I/O</abbr> que são relativamente "lentas" (comparadas a velocidade do processador e da memória RAM), como esperar por:
* dados do cliente para serem enviados através da rede
* dados enviados pelo seu programa para serem recebidos pelo clente através da rede
* conteúdo de um arquivo no disco pra ser lido pelo sistema e entregar ao seu programa
* conteúdo que seu programa deu ao sistema para ser escrito no disco
* uma operação remota API
* uma operação no banco de dados para finalizar
* uma solicitação no banco de dados esperando o retorno do resultado
* etc.
Enquanto o tempo de execução é consumido mais pela espera das operações <abbr title="Entrada e Saída">I/O</abbr>, essas operações são chamadas de operações "limitadas por I/O".
Isso é chamado de "assíncrono" porquê o computador / programa não tem que ser "sincronizado" com a tarefa lenta, esperando pelo exato momento que a tarefa finalize, enquanto não faz nada, para ser capaz de pegar o resultado da tarefa e dar continuidade ao trabalho.
Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar um pouco (alguns microssegundos) para que o computador / programa finalize o que quer que esteja fazendo,e então volte para pegar o resultado e continue trabalhando com ele.
Para "síncrono" (contrário de "assíncrono") também é utilizado o termo "sequencial", porquê o computador / programa segue todos os passos, na sequência, antes de trocar para uma tarefa diferente, mesmo se alguns passos envolvam esperar.
### Concorrência e hambúrgueres
Essa idéia de código **assíncrono** descrito acima é algo às vezes chamado de **"concorrência"**. E é diferente de **"paralelismo"**.
**Concorrência** e **paralelismo** ambos são relacionados a "diferentes coisas acontecendo mais ou menos ao mesmo tempo".
Mas os detalhes entre *concorrência* e *paralelismo* são bem diferentes.
Para ver essa diferença, imagine a seguinte história sobre hambúrgueres:
### Hambúrgueres concorrentes
Você vai com seu _crush_ :heart_eyes: na lanchonete, fica na fila enquanto o caixa pega os pedidos das pessoas na sua frente.
Então chega a sua vez, você pede dois saborosos hambúrgueres para você e seu _crush_ :heart_eyes:.
Você paga.
O caixa diz alguma coisa para o cara na cozinha para que ele tenha que preparar seus hambúrgueres (mesmo embora ele esteja preparando os lanches dos outros clientes).
O caixa te entrega seu número de chamada.
Enquanto você espera, você vai com seu _crush_ :heart_eyes: e pega uma mesa, senta e conversa com seu _crush_ :heart_eyes: por um bom tempo (como seus hambúrgueres são muito saborosos, leva um tempo para serem preparados).
Enquanto você está sentado na mesa com seu _crush_ :heart_eyes:, esperando os hambúrgueres, você pode gastar o tempo admirando como lindo, maravilhoso e esperto é seu _crush_ :heart_eyes:.
Enquanto espera e conversa com seu _crush_ :heart_eyes:, de tempos em tempos, você verifica o número de chamada exibido no balcão para ver se já é sua vez.
Então a certo ponto, é finalmente sua vez. Você vai no balcão, pega seus hambúrgueres e volta para a mesa.
Você e seu _crush_ :heart_eyes: comem os hambúrgueres e aproveitam o tempo.
---
Imagine que você seja o computador / programa nessa história.
Enquanto você está na fila, tranquilo, esperando por sua vez, não está fazendo nada "produtivo". Mas a fila é rápida porquê o caixa só está pegando os pedidos, então está tudo bem.
Então, quando é sua vez, você faz o trabalho "produtivo" de verdade, você processa o menu, decide o que quer, pega a escolha de seu _crush_ :heart_eyes:, paga, verifica se entregou o valor correto em dinheiro ou cartão de crédito, verifica se foi cobrado corretamente, verifica se seu pedido está correto etc.
Mas então, embora você ainda não tenha os hambúrgueres, seu trabalho no caixa está "pausado", porquê você tem que esperar seus hambúrgueres estarem prontos.
Mas enquanto você se afasta do balcão e senta na mesa com o número da sua chamada, você pode trocar sua atenção para seu _crush_ :heart_eyes:, e "trabalhar" nisso. Então você está novamente fazendo algo muito "produtivo", como flertar com seu _crush_ :heart_eyes:.
Então o caixa diz que "seus hambúrgueres estão prontos" colocando seu número no balcão, mas você não corre que nem um maluco imediatamente quando o número exibido é o seu. Você sabe que ninguém irá roubar seus hambúrgueres porquê você tem o número de chamada, e os outros tem os números deles.
Então você espera que seu _crush_ :heart_eyes: termine a história que estava contando (terminar o trabalho atual / tarefa sendo processada), sorri gentilmente e diz que você está indo buscar os hambúrgueres.
Então você vai no balcão, para a tarefa inicial que agora está finalizada, pega os hambúrgueres, e leva para a mesa. Isso finaliza esse passo / tarefa da interação com o balcão. Agora é criada uma nova tarefa, "comer hambúrgueres", mas a tarefa anterior, "pegar os hambúrgueres" já está finalizada.
### Hambúrgueres paralelos
Você vai com seu _crush_ :heart_eyes: em uma lanchonete paralela.
Você fica na fila enquanto alguns (vamos dizer 8) caixas pegam os pedidos das pessoas na sua frente.
Todo mundo antes de você está esperando pelos hambúrgueres estarem prontos antes de deixar o caixa porquê cada um dos 8 caixas vai e prepara o hambúrguer antes de pegar o próximo pedido.
Então é finalmente sua vez, e pede 2 hambúrgueres muito saborosos para você e seu _crush_ :heart_eyes:.
Você paga.
O caixa vai para a cozinha.
Você espera, na frente do balcão, para que ninguém pegue seus hambúrgueres antes de você, já que não tem números de chamadas.
Enquanto você e seu _crush_ :heart_eyes: estão ocupados não permitindo que ninguém passe a frente e pegue seus hambúrgueres assim que estiverem prontos, você não pode dar atenção ao seu _crush_ :heart_eyes:.
Isso é trabalho "síncrono", você está "sincronizado" com o caixa / cozinheiro. Você tem que esperar e estar lá no exato momento que o caixa / cozinheiro terminar os hambúrgueres e dá-los a você, ou então, outro alguém pode pegá-los.
Então seu caixa / cozinheiro finalmente volta com seus hambúrgueres, depois de um longo tempo esperando por eles em frente ao balcão.
Você pega seus hambúrgueres e vai para a mesa com seu _crush_ :heart_eyes:.
Vocês comem os hambúrgueres, e o trabalho está terminado.
Não houve muita conversa ou flerte já que a maior parte do tempo foi gasto esperando os lanches na frente do balcão.
---
Nesse cenário dos hambúrgueres paralelos, você é um computador / programa com dois processadores (você e seu _crush_ :heart_eyes:), ambos esperando e dedicando a atenção de estar "esperando no balcão" por um bom tempo.
A lanchonete paralela tem 8 processadores (caixas / cozinheiros). Enquanto a lanchonete dos hambúrgueres concorrentes tinham apenas 2 (um caixa e um cozinheiro).
Ainda assim, a última experiência não foi a melhor.
---
Essa poderia ser a história paralela equivalente aos hambúrgueres.
Para um exemplo "mais real", imagine um banco.
Até recentemente, a maioria dos bancos tinha muitos caixas e uma grande fila.
Todos os caixas fazendo todo o trabalho, um cliente após o outro.
E você tinha que esperar na fila por um longo tempo ou poderia perder a vez.
Você provavelmente não gostaria de levar seu _crush_ :heart_eyes: com você para um rolezinho no banco.
### Conclusão dos hambúrgueres
Nesse cenário dos "hambúrgueres com seu _crush_ :heart_eyes:", como tem muita espera, faz mais sentido ter um sistema concorrente.
Esse é o caso da maioria das aplicações web.
Geralmente são muitos usuários, e seu servidor está esperando pelas suas conexões não tão boas para enviar as requisições.
E então esperando novamente pelas respostas voltarem.
Essa "espera" é medida em microssegundos, e ainda assim, somando tudo, é um monte de espera no final.
Por isso que faz muito mais sentido utilizar código assíncrono para APIs web.
A maioria dos frameworks Python existentes mais populares (incluindo Flask e Django) foram criados antes que os novos recursos assíncronos existissem em Python. Então, os meios que eles podem ser colocados em produção para suportar execução paralela mais a forma antiga de execução assíncrona não são tão poderosos quanto as novas capacidades.
Mesmo embora a especificação principal para web assíncrono em Python (ASGI) foi desenvolvida no Django, para adicionar suporte para WebSockets.
Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e que essa seja a força do Go como uma linguagem de programa.
E esse é o mesmo nível de performance que você tem com o **FastAPI**.
E como você pode ter paralelismo e sincronicidade ao mesmo tempo, você tem uma maior performance do que a maioria dos frameworks NodeJS testados e lado a lado com Go, que é uma linguagem compilada próxima ao C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(tudo graças ao Starlette)</a>.
### Concorrência é melhor que paralelismo?
Não! Essa não é a moral da história.
Concorrência é diferente de paralelismo. E é melhor em cenários **específicos** que envolvam um monte de espera. Por isso, geralmente é muito melhor do que paralelismo para desenvolvimento de aplicações web. Mas não para tudo.
Então, para equilibrar tudo, imagine a seguinte historinha:
> Você tem que limpar uma grande casa suja.
*Sim, essa é toda a história*.
---
Não há espera em lugar algum, apenas um monte de trabalho para ser feito, em múltiplos cômodos da casa.
Você poderia ter chamadas como no exemplo dos hambúrgueres, primeiro a sala de estar, então a cozinha, mas você não está esperando por nada, apenas limpar e limpar, as chamadas não afetariam em nada.
Levaria o mesmo tempo para finalizar com ou sem chamadas (concorrência) e você teria feito o mesmo tanto de trabalho.
Mas nesse caso, se você trouxesse os 8 ex-caixas / cozinheiros / agora-faxineiros, e cada um deles (mais você) pudessem dividir a casa para limpá-la, vocês fariam toda a limpeza em **paralelo**, com a ajuda extra, e terminariam muito mais cedo.
Nesse cenário, cada um dos faxineiros (incluindo você) poderia ser um processador, fazendo a sua parte do trabalho.
E a maior parte do tempo de execução é tomada por trabalho (ao invés de ficar esperando), e o trabalho em um computador é feito pela <abbr title="Unidade de Processamento Central">CPU</abbr>, que podem gerar problemas que são chamados de "limite de CPU".
---
Exemplos comuns de limite de CPU são coisas que exigem processamento matemático complexo.
Por exemplo:
* **Processamento de áudio** ou **imagem**
* **Visão do Computador**: uma imagem é composta por milhões de pixels, cada pixel tem 3 valores (cores, processamento que normalmente exige alguma computação em todos esses pixels ao mesmo tempo)
* **Machine Learning**: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande folha de papel com números e multiplicando todos eles juntos e ao mesmo tempo.
* **Deep Learning**: Esse é um subcampo do Machine Learning, então o mesmo se aplica. A diferença é que não há apenas uma grande folha de papel com números para multiplicar, mas um grande conjunto de folhas de papel, e em muitos casos, você utiliza um processador especial para construir e/ou usar modelos.
### Concorrência + Paralelismo: Web + Machine learning
Com **FastAPI** você pode levar a vantagem da concorrência que é muito comum para desenvolvimento web (o mesmo atrativo de NodeJS).
Mas você também pode explorar os benefícios do paralelismo e multiprocessamento (tendo múltiplos processadores rodando em paralelo) para trabalhos pesados que geram **limite de CPU** como aqueles em sistemas de Machine Learning.
Isso, mais o simples fato que Python é a principal linguagem para **Data Science**, Machine Learning e especialmente Deep Learning, faz do FastAPI uma ótima escolha para APIs web e aplicações com Data Science / Machine Learning (entre muitas outras).
Para ver como alcançar esse paralelismo em produção veja a seção sobre [Deployment](deployment.md){.internal-link target=_blank}.
## `async` e `await`
Versões modernas do Python tem um modo muito intuitivo para definir código assíncrono. Isso faz parecer normal o código "sequencial" e fazer o "esperar" para você nos momentos certos.
Quando tem uma operação que exigirá espera antes de dar os resultados e tem suporte para esses recursos Python, você pode escrever assim:
```Python
burgers = await get_burgers(2)
```
A chave aqui é o `await`. Ele diz ao Python que ele tem que esperar por `get_burgers(2)` para finalizar suas coisas antes de armazenar os resultados em `burgers`. Com isso, o Python saberá que ele pode ir e fazer outras coisas nesse meio tempo (como receber outra requisição).
Para o `await` funcionar, tem que estar dentro de uma função que suporte essa assincronicidade. Para fazer isso, apenas declare a função com `async def`:
```Python hl_lines="1"
async def get_burgers(number: int):
# Fazer alguma coisa assíncrona para criar os hambúrgueres
return burgers
```
...ao invés de `def`:
```Python hl_lines="2"
# Isso não é assíncrono
def get_sequential_burgers(number: int):
# Faz alguma coisa sequencial para criar os hambúrgueres
return burgers
```
Com `async def`, o Python sabe que, dentro dessa função, tem que estar ciente das expressões `await`, e que isso pode "pausar" a execução dessa função, e poderá fazer outra coisa antes de voltar.
Quando você quiser chamar uma função `async def`, você tem que "esperar". Então, isso não funcionará:
```Python
# Isso não irá funcionar, porquê get_burgers foi definido com: async def
burgers = get_burgers(2)
```
---
Então, se você está usando uma biblioteca que diz que você pode chamá-la com `await`, você precisa criar as *funções de operação de rota* com `async def`, como em:
```Python hl_lines="2 3"
@app.get('/burgers')
async def read_burgers():
burgers = await get_burgers(2)
return burgers
```
### Mais detalhes técnicos
Você deve ter observado que `await` pode ser usado somente dentro de funções definidas com `async def`.
Mas ao mesmo tempo, funções definidas com `async def` tem que ser aguardadas. Então, funções com `async def` pdem ser chamadas somente dentro de funções definidas com `async def` também.
Então, sobre o ovo e a galinha, como você chama a primeira função async?
Se você estivar trabalhando com **FastAPI** não terá que se preocupar com isso, porquê essa "primeira" função será a sua *função de operação de rota*, e o FastAPI saberá como fazer a coisa certa.
Mas se você quiser usar `async` / `await` sem FastAPI, <a href="https://docs.python.org/3/library/asyncio-task.html#coroutine" class="external-link" target="_blank">verifique a documentação oficial Python</a>.
### Outras formas de código assíncrono
Esse estilo de usar `async` e `await` é relativamente novo na linguagem.
Mas ele faz o trabalho com código assíncrono muito mais fácil.
Essa mesma sintaxe (ou quase a mesma) foi também incluída recentemente em versões modernas do JavaScript (no navegador e NodeJS).
Mas antes disso, controlar código assíncrono era bem mais complexo e difícil.
Nas versões anteriores do Python, você poderia utilizar threads ou <a href="http://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Mas o código é um pouco mais complexo de entender, debugar, e pensar sobre.
Nas versões anteriores do NodeJS / Navegador JavaScript, você poderia utilizar "callbacks". O que leva ao <a href="http://callbackhell.com/" class="external-link" target="_blank">inferno do callback</a>.
## Corrotinas
**Corrotina** é apenas um jeito bonitinho para a coisa que é retornada de uma função `async def`. O Python sabe que é uma função que pode começar e terminar em algum ponto, mas que pode ser pausada internamente também, sempre que tiver um `await` dentro dela.
Mas toda essa funcionalidade de código assíncrono com `async` e `await` é muitas vezes resumida como "corrotina". É comparável ao principal recurso chave do Go, a "Gorotina".
## Conclusão
Vamos ver a mesma frase com o conteúdo cima:
> Versões modernas do Python tem suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**.
Isso pode fazer mais sentido agora.
Tudo isso é o que deixa o FastAPI poderoso (através do Starlette) e que o faz ter uma performance impressionante.
## Detalhes muito técnicos
!!! warning
Você pode provavelmente pular isso.
Esses são detalhes muito técnicos de como **FastAPI** funciona por baixo do capô.
Se você tem algum conhecimento técnico (corrotinas, threads, blocking etc) e está curioso sobre como o FastAPI controla o `async def` vs normal `def`, vá em frente.
### Funções de operação de rota
Quando você declara uma *função de operação de rota* com `def` normal ao invés de `async def`, ela é rodada em uma threadpool externa que então é aguardada, ao invés de ser chamada diretamente (ela poderia bloquear o servidor).
Se você está chegando de outro framework assíncrono que não faz o trabalho descrito acima e você está acostumado a definir triviais *funções de operação de rota* com simples `def` para ter um mínimo ganho de performance (cerca de 100 nanosegundos), por favor observe que no **FastAPI** o efeito pode ser bem o oposto. Nesses casos, é melhor usar `async def` a menos que suas *funções de operação de rota* utilizem código que performem bloqueamento <abbr title="Input/Output: disco lendo ou escrevendo, comunicações de rede.">IO</abbr>.
Ainda, em ambas as situações, as chances são que o **FastAPI** será [ainda mais rápido](/#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seus frameworks antecessores.
### Dependências
O mesmo se aplica para as dependências. Se uma dependência tem as funções com padrão `def` ao invés de `async def`, ela é rodada no threadpool externo.
### Sub-dependências
Você pode ter múltiplas dependências e sub-dependências exigindo uma a outra (como parâmetros de definições de funções), algumas delas podem ser criadas com `async def` e algumas com `def` normal. Isso ainda poderia funcionar, e aquelas criadas com `def` podem ser chamadas em uma thread externa ao invés de serem "aguardadas".
### Outras funções de utilidade
Qualquer outra função de utilidade que você chame diretamente pode ser criada com `def` normal ou `async def` e o FastAPI não irá afetar o modo como você a chama.
Isso está em contraste às funções que o FastAPI chama para você: *funções de operação de rota* e dependências.
Se sua função de utilidade é uma função normal com `def`, ela será chamada diretamente (como você a escreve no código), não em uma threadpool, se a função é criada com `async def` então você deve esperar por essa função quando você chamá-la no seu código.
---
Novamente, esses são detalhes muito técnicos que provavelmente possam ser úteis caso você esteja procurando por eles.
Caso contrário, você deve ficar bem com as dicas da seção acima: <a href="#in-a-hurry">Com pressa?</a>.

View File

@@ -0,0 +1,48 @@
# Sobre HTTPS
É fácil assumir que HTTPS é algo que é apenas "habilitado" ou não.
Mas é bem mais complexo do que isso.
!!! tip "Dica"
Se você está com pressa ou não se importa, continue com as seções seguintes para instruções passo a passo para configurar tudo com diferentes técnicas.
Para aprender o básico de HTTPS de uma perspectiva do usuário, verifique <a href="https://howhttps.works/pt-br/" class="external-link" target="_blank">https://howhttps.works/pt-br/</a>.
Agora, a partir de uma perspectiva do desenvolvedor, aqui estão algumas coisas para ter em mente ao pensar em HTTPS:
* Para HTTPS, o servidor precisa ter certificados gerados por um terceiro.
* Esses certificados são adquiridos de um terceiro, eles não são simplesmente "gerados".
* Certificados têm um tempo de vida.
* Eles expiram.
* E então eles precisam ser renovados, adquirindo-os novamente de um terceiro.
* A criptografia da conexão acontece no nível TCP.
* Essa é uma camada abaixo do HTTP.
* Portanto, o manuseio do certificado e da criptografia é feito antes do HTTP.
* O TCP não sabe sobre "domínios". Apenas sobre endereços IP.
* As informações sobre o domínio solicitado vão nos dados HTTP.
* Os certificados HTTPS “certificam” um determinado domínio, mas o protocolo e a encriptação acontecem ao nível do TCP, antes de sabermos de que domínio se trata.
* Por padrão, isso significa que você só pode ter um certificado HTTPS por endereço IP.
* Não importa o tamanho do seu servidor ou quão pequeno cada aplicativo que você tem nele possa ser.
* No entanto, existe uma solução para isso.
* Há uma extensão para o protocolo TLS (aquele que lida com a criptografia no nível TCP, antes do HTTP) chamado <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>.
* Esta extensão SNI permite que um único servidor (com um único endereço IP) tenha vários certificados HTTPS e atenda a vários domínios / aplicativos HTTPS.
* Para que isso funcione, um único componente (programa) em execução no servidor, ouvindo no endereço IP público, deve ter todos os certificados HTTPS no servidor.
* Depois de obter uma conexão segura, o protocolo de comunicação ainda é HTTP.
* Os conteúdos são criptografados, embora sejam enviados com o protocolo HTTP.
É uma prática comum ter um programa/servidor HTTP em execução no servidor (máquina, host, etc.) e gerenciar todas as partes HTTPS: enviando as solicitações HTTP descriptografadas para o aplicativo HTTP real em execução no mesmo servidor (a aplicação **FastAPI**, neste caso), pegue a resposta HTTP do aplicativo, criptografe-a usando o certificado apropriado e envie-a de volta ao cliente usando HTTPS. Este servidor é frequentemente chamado de <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>.
## Let's Encrypt
Antes de Let's Encrypt, esses certificados HTTPS eram vendidos por terceiros confiáveis.
O processo de aquisição de um desses certificados costumava ser complicado, exigia bastante papelada e os certificados eram bastante caros.
Mas então <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> foi criado.
Ele é um projeto da Linux Foundation que fornece certificados HTTPS gratuitamente. De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a segurança é realmente melhor por causa de sua vida útil reduzida.
Os domínios são verificados com segurança e os certificados são gerados automaticamente. Isso também permite automatizar a renovação desses certificados.
A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha HTTPS seguro, de graça e para sempre.

View File

@@ -0,0 +1,178 @@
# Pessoas do FastAPI
FastAPI possue uma comunidade incrível que recebe pessoas de todos os níveis.
## Criador - Mantenedor
Ei! 👋
Este sou eu:
{% if people %}
<div class="user-list user-list-center">
{% for user in people.maintainers %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Respostas: {{ user.answers }}</div><div class="count">Pull Requests: {{ user.prs }}</div></div>
{% endfor %}
</div>
{% endif %}
Eu sou o criador e mantenedor do **FastAPI**. Você pode ler mais sobre isso em [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}.
...Mas aqui eu quero mostrar a você a comunidade.
---
**FastAPI** recebe muito suporte da comunidade. E quero destacar suas contribuições.
Estas são as pessoas que:
* [Help others with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}.
* [Create Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}.
* Revisar Pull Requests, [especially important for translations](contributing.md#translations){.internal-link target=_blank}.
Uma salva de palmas para eles. 👏 🙇
## Usuários mais ativos do ultimo mês
Estes são os usuários que estão [helping others the most with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} durante o ultimo mês. ☕
{% if people %}
<div class="user-list user-list-center">
{% for user in people.last_month_active %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Issues respondidas: {{ user.count }}</div></div>
{% endfor %}
</div>
{% endif %}
## Especialistas
Aqui está os **Especialistas do FastAPI**. 🤓
Estes são os usuários que [helped others the most with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} em *todo o tempo*.
Eles provaram ser especialistas ajudando muitos outros. ✨
{% if people %}
<div class="user-list user-list-center">
{% for user in people.experts %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Issues respondidas: {{ user.count }}</div></div>
{% endfor %}
</div>
{% endif %}
## Top Contribuidores
Aqui está os **Top Contribuidores**. 👷
Esses usuários têm [created the most Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} que tem sido *mergeado*.
Eles contribuíram com o código-fonte, documentação, traduções, etc. 📦
{% if people %}
<div class="user-list user-list-center">
{% for user in people.top_contributors %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Pull Requests: {{ user.count }}</div></div>
{% endfor %}
</div>
{% endif %}
Existem muitos outros contribuidores (mais de uma centena), você pode ver todos eles em <a href="https://github.com/tiangolo/fastapi/graphs/contributors" class="external-link" target="_blank">Página de Contribuidores do FastAPI no GitHub</a>. 👷
## Top Revisores
Esses usuários são os **Top Revisores**. 🕵️
### Revisões para Traduções
Eu só falo algumas línguas (e não muito bem 😅). Então, os revisores são aqueles que têm o [**poder de aprovar traduções**](contributing.md#translations){.internal-link target=_blank} da documentação. Sem eles, não haveria documentação em vários outros idiomas.
---
Os **Top Revisores** 🕵️ revisaram a maior parte de Pull Requests de outros, garantindo a qualidade do código, documentação, e especialmente, as **traduções**.
{% if people %}
<div class="user-list user-list-center">
{% for user in people.top_reviewers %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Revisões: {{ user.count }}</div></div>
{% endfor %}
</div>
{% endif %}
## Patrocinadores
Esses são os **Patrocinadores**. 😎
Eles estão apoiando meu trabalho **FastAPI** (e outros), principalmente através de <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>.
{% if sponsors %}
{% if sponsors.gold %}
### Patrocinadores Ouro
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}"></a>
{% endfor %}
{% endif %}
{% if sponsors.silver %}
### Patrocinadores Prata
{% for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}"></a>
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### Patrocinadores Bronze
{% for sponsor in sponsors.bronze -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}"></a>
{% endfor %}
{% endif %}
### Patrocinadores Individuais
{% if github_sponsors %}
{% for group in github_sponsors.sponsors %}
<div class="user-list user-list-center">
{% for user in group %}
{% if user.login not in sponsors_badge.logins %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
{% endif %}
{% endif %}
## Sobre os dados - detalhes técnicos
A principal intenção desta página é destacar o esforço da comunidade para ajudar os outros.
Especialmente incluindo esforços que normalmente são menos visíveis, e em muitos casos mais árduo, como ajudar os outros com issues e revisando Pull Requests com traduções.
Os dados são calculados todo mês, você pode ler o <a href="https://github.com/tiangolo/fastapi/blob/master/.github/actions/people/app/main.py" class="external-link" target="_blank">código fonte aqui</a>.
Aqui também estou destacando contribuições de patrocinadores.
Eu também me reservo o direito de atualizar o algoritmo, seções, limites, etc (só para prevenir 🤷).

View File

@@ -32,7 +32,6 @@ Se você precisa refrescar a memória rapidamente sobre como usar tipos do Pytho
Você escreve Python padrão com tipos:
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel

View File

@@ -39,7 +39,7 @@ Os recursos chave são:
<small>* estimativas baseadas em testes realizados com equipe interna de desenvolvimento, construindo aplicações em produção.</small>
## Sponsors
## Patrocinadores Ouro
<!-- sponsors -->
@@ -54,7 +54,7 @@ Os recursos chave são:
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
<a href="https://fastapi.tiangolo.com/pt/fastapi-people/#patrocinadores" class="external-link" target="_blank">Outros patrocinadores</a>
## Opiniões

View File

@@ -0,0 +1,246 @@
# Parâmetros da rota da URL
Você pode declarar os "parâmetros" ou "variáveis" com a mesma sintaxe utilizada pelo formato de strings do Python:
```Python hl_lines="6-7"
{!../../../docs_src/path_params/tutorial001.py!}
```
O valor do parâmetro que foi passado à `item_id` será passado para a sua função como o argumento `item_id`.
Então, se você rodar este exemplo e for até <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, você verá a seguinte resposta:
```JSON
{"item_id":"foo"}
```
## Parâmetros da rota com tipos
Você pode declarar o tipo de um parâmetro na função usando as anotações padrões do Python:
```Python hl_lines="7"
{!../../../docs_src/path_params/tutorial002.py!}
```
Nesse caso, `item_id` está sendo declarado como um `int`.
!!! Check Verifique
Isso vai dar à você suporte do seu editor dentro das funções, com verificações de erros, autocompletar, etc.
## Conversão de <abbr title="também conhecido como: serialização, parsing, marshalling">dados</abbr>
Se você rodar esse exemplo e abrir o seu navegador em <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, você verá a seguinte resposta:
```JSON
{"item_id":3}
```
!!! Verifique
Observe que o valor recebido pela função (e também retornado por ela) é `3`, como um Python `int`, não como uma string `"3"`.
Então, com essa declaração de tipo, o **FastAPI** dá pra você um <abbr title="convertendo a string que veio do request HTTP em um dado Python">"parsing"</abbr> automático no request .
## Validação de dados
Mas se você abrir o seu navegador em <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, você verá um belo erro HTTP:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
devido ao parâmetro da rota `item_id` ter um valor `"foo"`, que não é um `int`.
O mesmo erro apareceria se você tivesse fornecido um `float` ao invés de um `int`, como em: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
!!! Verifique
Então, com a mesma declaração de tipo do Python, o **FastAPI** dá pra você validação de dados.
Observe que o erro também mostra claramente o ponto exato onde a validação não passou.
Isso é incrivelmente útil enquanto se desenvolve e debuga o código que interage com a sua API.
## Documentação
Quando você abrir o seu navegador em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, você verá de forma automática e interativa a documtação da API como:
<img src="/img/tutorial/path-params/image01.png">
!!! check
Novamente, apenas com a mesma declaração de tipo do Python, o **FastAPI** te dá de forma automática e interativa a documentação (integrada com o Swagger UI).
Veja que o parâmetro de rota está declarado como sendo um inteiro (int).
## Beneficios baseados em padrões, documentação alternativa
Devido ao schema gerado ser o padrão do <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a>, existem muitas ferramentas compatíveis.
Por esse motivo, o próprio **FastAPI** fornece uma API alternativa para documentação (utilizando ReDoc), que você pode acessar em <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
<img src="/img/tutorial/path-params/image02.png">
Da mesma forma, existem muitas ferramentas compatíveis. Incluindo ferramentas de geração de código para muitas linguagens.
## Pydantic
Toda a validação de dados é feita por baixo dos panos pelo <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>, então você tem todos os benefícios disso. E assim você sabe que está em boas mãos.
Você pode usar as mesmas declarações de tipo com `str`, `float`, `bool` e muitos outros tipos complexos de dados.
Vamos explorar muitos destes tipos nos próximos capítulos do tutorial.
## A ordem importa
Quando você cria operações de rota, você pode se deparar com situações onde você pode ter uma rota fixa.
Algo como `/users/me` por exemplo, digamos que essa rota seja utilizada para pegar dados sobre o usuário atual.
E então você pode ter também uma rota `/users/{user_id}` para pegar dados sobre um usuário específico associado a um ID de usuário.
Porque as operações de rota são avaliadas em ordem, você precisa ter certeza que a rota para `/users/me` está sendo declarado antes da rota `/users/{user_id}`:
```Python hl_lines="6 11"
{!../../../docs_src/path_params/tutorial003.py!}
```
Caso contrário, a rota para `/users/{user_id}` coincidiria também para `/users/me`, "pensando" que estaria recebendo o parâmetro `user_id` com o valor de `"me"`.
## Valores predefinidos
Se você tem uma operação de rota que recebe um parâmetro da rota, mas que você queira que esses valores possíveis do parâmetro da rota sejam predefinidos, você pode usar <abbr title="Enumeration">`Enum`</abbr> padrão do Python.
### Criando uma classe `Enum`
Importe `Enum` e crie uma sub-classe que herde de `str` e de `Enum`.
Por herdar de `str` a documentação da API vai ser capaz de saber que os valores devem ser do tipo `string` e assim ser capaz de mostrar eles corretamente.
Assim, crie atributos de classe com valores fixos, que serão os valores válidos disponíveis.
```Python hl_lines="1 6-9"
{!../../../docs_src/path_params/tutorial005.py!}
```
!!! informação
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerations (ou enums) estão disponíveis no Python</a> desde a versão 3.4.
!!! dica
Se você está se perguntando, "AlexNet", "ResNet", e "LeNet" são apenas nomes de <abbr title="técnicamente, modelos de arquitetura de Deep Learning">modelos</abbr> de Machine Learning (aprendizado de máquina).
### Declare um *parâmetro de rota*
Logo, crie um *parâmetro de rota* com anotações de tipo usando a classe enum que você criou (`ModelName`):
```Python hl_lines="16"
{!../../../docs_src/path_params/tutorial005.py!}
```
### Revise a documentação
Visto que os valores disponíveis para o parâmetro da rota estão predefinidos, a documentação interativa pode mostrar esses valores de uma forma bem legal:
<img src="/img/tutorial/path-params/image03.png">
### Trabalhando com os *enumeration* do Python
O valor do *parâmetro da rota* será um *membro de enumeration*.
#### Compare *membros de enumeration*
Você pode comparar eles com o *membro de enumeration* no enum `ModelName` que você criou:
```Python hl_lines="17"
{!../../../docs_src/path_params/tutorial005.py!}
```
#### Obtenha o *valor de enumerate*
Você pode ter o valor exato de enumerate (um `str` nesse caso) usando `model_name.value`, ou em geral, `your_enum_member.value`:
```Python hl_lines="20"
{!../../../docs_src/path_params/tutorial005.py!}
```
!!! conselho
Você também poderia acessar o valor `"lenet"` com `ModelName.lenet.value`
#### Retorne *membros de enumeration*
Você pode retornar *membros de enum* da sua *rota de operação*, em um corpo JSON aninhado (por exemplo um `dict`).
Eles serão convertidos para o seus valores correspondentes (strings nesse caso) antes de serem retornados ao cliente:
```Python hl_lines="18 21 23"
{!../../../docs_src/path_params/tutorial005.py!}
```
No seu cliente você vai obter uma resposta JSON como:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Parâmetros de rota que contém caminhos
Digamos que você tenha uma *operação de rota* com uma rota `/files/{file_path}`.
Mas você precisa que o próprio `file_path` contenha uma *rota*, como `home/johndoe/myfile.txt`.
Então, a URL para este arquivo deveria ser algo como: `/files/home/johndoe/myfile.txt`.
### Suporte do OpenAPI
O OpenAPI não suporta uma maneira de declarar um *parâmetro de rota* que contenha uma *rota* dentro, dado que isso poderia levar a cenários que são difíceis de testar e definir.
No entanto, você pode fazer isso no **FastAPI**, usando uma das ferramentas internas do Starlette.
A documentação continuaria funcionando, ainda que não adicionaria nenhuma informação dizendo que o parâmetro deveria conter uma rota.
### Conversor de rota
Usando uma opção direta do Starlette você pode declarar um *parâmetro de rota* contendo uma *rota* usando uma URL como:
```
/files/{file_path:path}
```
Nesse caso, o nome do parâmetro é `file_path`, e a última parte, `:path`, diz que o parâmetro deveria coincidir com qualquer *rota*.
Então, você poderia usar ele com:
```Python hl_lines="6"
{!../../../docs_src/path_params/tutorial004.py!}
```
!!! dica
Você poderia precisar que o parâmetro contivesse `/home/johndoe/myfile.txt`, com uma barra no inicio (`/`).
Neste caso, a URL deveria ser: `/files//home/johndoe/myfile.txt`, com barra dupla (`//`) entre `files` e `home`.
## Recapitulando
Com o **FastAPI**, usando as declarações de tipo do Python, você obtém:
* Suporte no editor: verificação de erros, e opção de autocompletar, etc.
* Parsing de dados
* "<abbr title="convertendo uma string que vem de um request HTTP em dado Python">Parsing</abbr>" de dados
* Validação de dados
* Anotação da API e documentação automática
Você apenas tem que declará-los uma vez.
Essa é provavelmente a vantagem mais visível do **FastAPI** se comparado com frameworks alternativos (além do desempenho puro).

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -54,15 +55,20 @@ nav:
- uk: /uk/
- zh: /zh/
- features.md
- fastapi-people.md
- Tutorial - Guia de Usuário:
- tutorial/index.md
- tutorial/first-steps.md
- tutorial/path-params.md
- tutorial/body-fields.md
- Segurança:
- tutorial/security/index.md
- Guia de Usuário Avançado:
- advanced/index.md
- Implantação:
- deployment/index.md
- deployment/versions.md
- deployment/https.md
- alternatives.md
- history-design-future.md
- external-links.md
@@ -72,7 +78,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -81,7 +87,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -134,6 +140,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -58,7 +59,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +68,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +121,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -58,7 +59,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +68,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +121,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -0,0 +1,34 @@
# Kıyaslamalar
Bağımsız TechEmpower kıyaslamaları gösteriyor ki Uvicorn'la beraber çalışan **FastAPI** uygulamaları <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">Python'un en hızlı frameworklerinden birisi </a>, sadece Starlette ve Uvicorn'dan daha düşük sıralamada (FastAPI bu frameworklerin üzerine kurulu). (*)
Fakat kıyaslamaları ve karşılaştırmaları incelerken şunları aklınızda bulundurmalısınız.
## Kıyaslamalar ve hız
Kıyaslamaları incelediğinizde, farklı özelliklere sahip birçok araçların eşdeğer olarak karşılaştırıldığını görmek yaygındır.
Özellikle, Uvicorn, Starlette ve FastAPI'ın birlikte karşılaştırıldığını görmek için (diğer birçok araç arasında).
Araç tarafından çözülen sorun ne kadar basitse, o kadar iyi performans alacaktır. Ve kıyaslamaların çoğu, araç tarafından sağlanan ek özellikleri test etmez.
Hiyerarşi şöyledir:
* **Uvicorn**: bir ASGI sunucusu
* **Starlette**: (Uvicorn'u kullanır) bir web microframeworkü
* **FastAPI**: (Starlette'i kullanır) data validation vb. ile API'lar oluşturmak için çeşitli ek özelliklere sahip bir API frameworkü
* **Uvicorn**:
* Sunucunun kendisi dışında ekstra bir kod içermediği için en iyi performansa sahip olacaktır
* Direkt olarak Uvicorn'da bir uygulama yazmazsınız. Bu, en azından Starlette tarafından sağlanan tüm kodu (veya **FastAPI**) az çok içermesi gerektiği anlamına gelir. Ve eğer bunu yaptıysanız, son uygulamanız bir framework kullanmak ve uygulama kodlarını ve bugları en aza indirmekle aynı ek yüke sahip olacaktır.
* Eğer Uvicorn'u karşılaştırıyorsanız, Daphne, Hypercorn, uWSGI, vb. uygulama sunucuları ile karşılaştırın.
* **Starlette**:
* Uvicorn'dan sonraki en iyi performansa sahip olacak. Aslında, Starlette çalışmak için Uvicorn'u kullanıyor. Dolayısıyla, muhtemelen daha fazla kod çalıştırmak zorunda kaldığında Uvicorn'dan sadece "daha yavaş" olabilir.
* Ancak routing based on paths ile vb. basit web uygulamaları oluşturmak için araçlar sağlar.
* Eğer Starlette'i karşılaştırıyorsanız, Sanic, Flask, Django, vb. frameworkler (veya microframeworkler) ile karşılaştırın.
* **FastAPI**:
* Starlette'in Uvicorn'u kullandığı ve ondan daha hızlı olamayacağı gibi, **FastAPI** da Starlette'i kullanır, bu yüzden ondan daha hızlı olamaz.
* FastAPI, Starlette'e ek olarak daha fazla özellik sunar. Data validation ve serialization gibi API'lar oluştururken neredeyse ve her zaman ihtiyaç duyduğunuz özellikler. Ve bunu kullanarak, ücretsiz olarak otomatik dokümantasyon elde edersiniz (otomatik dokümantasyon çalışan uygulamalara ek yük getirmez, başlangıçta oluşturulur).
* FastAPI'ı kullanmadıysanız ve Starlette'i doğrudan kullandıysanız (veya başka bir araç, Sanic, Flask, Responder, vb.) tüm data validation'ı ve serialization'ı kendiniz sağlamanız gerekir. Dolayısıyla, son uygulamanız FastAPI kullanılarak oluşturulmuş gibi hâlâ aynı ek yüke sahip olacaktır. Çoğu durumda, uygulamalarda yazılan kodun büyük çoğunluğunu data validation ve serialization oluşturur.
* Dolayısıyla, FastAPI'ı kullanarak geliştirme süresinden, buglardan, kod satırlarından tasarruf edersiniz ve muhtemelen kullanmasaydınız aynı performansı (veya daha iyisini) elde edersiniz. (hepsini kodunuza uygulamak zorunda kalacağınız gibi)
* Eğer FastAPI'ı karşılaştırıyorsanız, Flask-apispec, NestJS, Molten, vb. gibi data validation, serialization ve dokümantasyon sağlayan bir web uygulaması frameworkü ile (veya araç setiyle) karşılaştırın. Entegre otomatik data validation, serialization ve dokümantasyon içeren frameworkler.

209
docs/tr/docs/features.md Normal file
View File

@@ -0,0 +1,209 @@
# Özelikler
## FastAPI özellikleri
**FastAPI** sana bunları sağlıyor
### Açık standartları temel alır
* API oluşturma işlemlerinde <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> buna <abbr title="also known as: endpoints, routes">path</abbr> <abbr title=" HTTP metodları olarak bilinen, POST, GET, PUT, DELETE">operasyonları </abbr>parametreleri, body talebi, güvenlik gibi şeyler dahil olmak üzere deklare bunların deklare edilmesi.
* Otomatik olarak data modelinin <a href="http://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> ile beraber dokümante edilmesi (OpenAPI'n kendisi zaten JSON Schema'ya dayanıyor).
* Titiz bir çalışmanın sonucunda yukarıdaki standartlara uygun bir framework oluşturduk. Standartları pastanın üzerine sonradan eklenmiş bir çilek olarak görmedik.
* Ayrıca bu bir çok dilde kullanılabilecek **client code generator** kullanımına da izin veriyor.
### Otomatik dokümantasyon
OpenAPI standartlarına dayalı olan bir framework olarak, geliştiricilerin birden çok seçeneği var, varsayılan olarak gelen 2 farklı interaktif API dokümantasyonu ve web kullanıcı arayüzü var.
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a> interaktif olarak API'ınızı tarayıcı üzerinden çağırıp test edebilmenize olanak sağlıyor.
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a> ile beraber alternatif API dokümantasyonu.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Sadece modern Python
Tamamiyle standartlar **Python 3.6**'nın type hintlerine dayanıyor (Pydantic'in sayesinde). Yeni bir syntax öğrenmene gerek yok. Sadece modern Python.
Eğer Python type hintlerini bilmiyorsan veya bir hatırlatmaya ihtiyacın var ise(FastAPI kullanmasan bile) şu iki dakikalık küçük bilgilendirici içeriğe bir göz at: [Python Types](python-types.md){.internal-link target=_blank}.
Standart Python'u typelarını belirterek yazıyorsun:
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel
# Değişkeni str olarak belirt
# ve o fonksiyon için harika bir editör desteği al
def main(user_id: str):
return user_id
# Pydantic modeli
class User(BaseModel):
id: int
name: str
joined: date
```
Sonrasında bu şekilde kullanabilirsin
```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` şu anlama geliyor:
Key-Value çiftini direkt olarak `second_user_data` dictionarysine kaydet , yaptığın şey buna eşit olacak: `User(id=4, name="Mary", joined="2018-11-30")`
### Editor desteği
Bütün framework kullanılması kolay ve sezgileri güçlü olması için tasarlandı, verilen bütün kararlar geliştiricilere en iyi geliştirme deneyimini yaşatmak üzere, bir çok editör üzerinde test edildi.
Son yapılan Python geliştiricileri anketinde, açık ara <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">en çok kullanılan özellik "oto-tamamlama" idi.</a>.
Bütün **FastAPI** frameworkü oto-tamamlama açısından geliştiriciyi tatmin etmek üzerine tasarlandı. Otomatik tamamlama her yerde çalışıyor.
Dokümantasyona tekrardan çok nadir olarak geleceksin.
Editörün sana nasıl yardım ettiğine bir bak:
* <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a> ile:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
* <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> ile:
![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png)
Daha önceden düşünüp en imkansız diyebileceğin durumlarda bile otomatik tamamlama alacaksın, örnek olarak `price` JSON body içerisinde (nested bir JSON body de olabilirdi.) direkt olarak istekten geliyor, bu durumda bile oto-tammalama sağlıyor.
Artık key isimlerini yanlış yazma, dokümantasyona dönüp deliler gibi yukarı aşağı sayfada gezmek ve en sonunda `username` mi yoksa `user_name` mi kullandım gibi sorular yok.
### Kısa
Her şey için mantıklı bir **varsayılanı** var. Parametrelerini opsiyonel olarak tanımlayıp API'nı istediğin gibi modifiye edebilirsin.
Hepsi varsayılan olarak **çalışıyor**.
### Doğrulama
* Neredeyse bütün (ya da hepsi?) Python **data typeları** için doğrulama, kapsadıkları:
* JSON objeleri (`dict`).
* JSON array (`list`) item type'ı belirtirken.
* String (`str`) parametresi, minimum ve maksimum uzunluk gibi sınırlandırmalar yaparken.
* Numaralar (`int`, `float`) maksimum ve minimum gibi sınırlandırmalar yaparken.
* Bunlar gibi en egzotik typelarla bile doğrulama yapabiliyorsunuz.:
* URL.
* Email.
* UUID.
* ...ve diğerleri.
Bütün doğrulama olayları çok güçlü bir kütüphane sayesinde yapılıyor, **Pydantic**.
### Güvenlik ve kimlik doğrulama
Güvenlik ve doğrulama database ve data modellerinden taviz vermeden entegre edilebilir durumda.
Bütün güvenlik şemaları OpenAPI'da tanımlanmış durumda, kapsadıkları:
* HTTP Basic.
* **OAuth2** (ve **JWT tokenleriyle** beraber). Bu öğretici içeriğe göz atabilirsin [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
* API anahtarları:
* Headerlar.
* Query parametreleri.
* Cookies, vs.
Bütün güvenlik özellikleri Starlette'den geliyor (**session cookies'de** dahil olmak üzere).
Bütün hepsi tekrardan kullanılabilir aletler ve bileşenler olarak, kolayca sistemlerinize, data depolarınıza, ilişkisel ve NoSQL databaselerinize entegre edebileceğiniz şekilde yapıldı.
### Dependency injection
FastAPI'ın inanılmaz derecede kullanımı kolay, fakat inanılmaz derecede güçlü <abbr title='"components", "resources", "services", "providers" olarak da bilinen'><strong>Dependency Injection </strong></abbr> sistemi var.
* Dependencylerin bile dependencies'i olabiliyor, FastAPI bunun için **graph of "dependency"** yaratıyor.
* Hepsi **otomatik olarak** FastAPI tarafından hallediliyor.
* Bütün zorunlulukların gelen datalara bağlı olarak farklı gereksinimleri olabiliyor, ilave path operasyonlarının kısıtlamaları ve otomatik dokümantasyonu da ayrıca yapılıyor .
* Path operasyonu parametreleri içerisinde belirtilen gereksinimler için bile **Otomatik doğrulama** yapılabiliyor.
* Kompleks kimlik doğrulama sistemleri için destek, **database bağlantıları**, vs.
* **Taviz yok** hiçbir şeyden taviz vermeden, database frontend vs. Bütün hepsinin kolayca entegre edilebiliyor.
### Sınırsız "plug-inler"
Başka bir deyişle, plug-inlere ihtiyacımız yok, import edip direkt olarak kullanmaya başlayabiliriz.
Bütün entegrasyonlar kullanımı kolay olmak üzere (zorunluluklar ile beraber) tasarlandı, sen bir "plug-in" yaratıp 2 satır kod ile, *path operasyonlarında* kullandığımız syntax ve aynı yapı ile koduna entregre edebilirsin.
### Test edildi
* 100% <abbr title="Kodun ne kadarının test edildiği">test coverage</abbr>.
* 100% <abbr title="Python type annotations, with this your editor and external tools can give you better support">typeları belirtilmiş</abbr> codebase.
* FastAPI ile yapılan bir çok proje insanlar tarafından kullanılıyor.
## Starlette özellikleri
**FastAPI**, <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a> ile tamamiyle uyumlu ve üzerine kurulu. Yani FastAPI üzerine ekleme yapacağınız herhangi bir Starlette kodu da çalışacaktır.
`FastAPI` aslında `Starlette`'nin bir sub-class'ı. Eğer Starlette'nin nasıl kullanılacağını biliyor isen, çoğu işlevini aynı şekilde yapıyor.
**FastAPI** ile beraber **Starlette**'nin bütün özelliklerine de sahip olacaksınız (FastAPI aslında Starlette'nin steroid basmış hali):
* Gerçekten etkileyici bir performansa sahip.Python'un ise en hızlı frameworklerinden bir tanesi, <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">**NodeJS** ve **Go** ile ise eşdeğer performansa sahip.</a>.
* **WebSocket** desteği.
* **GraphQL** desteği.
* Kullanım halinde arka plan işlevleri.
* Başlatma ve kapatma eventleri(startup and shutdown).
* Test sunucusu `requests` üzerine kurulu.
* **CORS**, GZip, Static dosyalar, Streaming responseları.
* **Session and Cookie** desteği.
* 100% test kapsayıcılığı.
* 100% typeları belirtilmiş codebase.
## Pydantic özellikleri
**FastAPI** ile <a href="https://pydantic-docs.helpmanual.io" class="external-link" target="_blank"><strong>Pydantic</strong></a> tamamiyle uyumlu ve üzerine kurulu. Yani FastAPI üzerine ekleme yapacağınız herhangi bir Pydantic kodu da çalışacaktır.
Bunlara Pydantic üzerine kurulu <abbr title="Object-Relational Mapper">ORM</abbr> databaseler ve , <abbr title="Object-Document Mapper">ODM</abbr> kütüphaneler de dahil olmak üzere.
Bu ayrıca şu anlama da geliyor, bir çok durumda requestten gelen objeyi **direkt olarak database**'e her şeyi otomatik olarak doğrulanmış bir biçimde aktarabilirisin.
Aynı şekilde, databaseden gelen objeyi de **direkt olarak isteğe** de tamamiyle doğrulanmış bir biçimde gönderebilirsiniz.
**FastAPI** ile beraber **Pydantic**'in bütün özelliklerine sahip olacaksınız (FastAPI data kontrolünü Pydantic'in üzerine kurduğu için):
* **Kafa karıştırmaz**:
* Farklı bir syntax öğrenmenize gerek kalmaz,
* Eğer Python typelarını nasıl kullanacağını biliyorsan Pydantic kullanmayı da biliyorsundur.
* Kullandığın geliştirme araçları ile iyi çalışır **<abbr title="Integrated Development Environment, kod editörüne benzer">IDE</abbr>/<abbr title="Code errorlarınızı inceleyen program">linter</abbr>/brain**:
* Pydantic'in veri yapıları aslında sadece senin tanımladığın classlar; Bu yüzden doğrulanmış dataların ile otomatik tamamlama, linting ve mypy'ı kullanarak sorunsuz bir şekilde çalışabilirsin
* **Hızlı**:
* <a href="https://pydantic-docs.helpmanual.io/#benchmarks-tag" class="external-link" target="_blank">Benchmarklarda</a>, Pydantic'in diğer bütün test edilmiş bütün kütüphanelerden daha hızlı.
* **En kompleks** yapıları bile doğrula:
* Hiyerarşik Pydantic modellerinin kullanımı ile beraber, Python `typing`s `List` and `Dict`, vs gibi şeyleri doğrula.
* Doğrulayıcılar en kompleks data şemalarının bile temiz ve kolay bir şekilde tanımlanmasına izin veriyor, ve hepsi JSON şeması olarak dokümante ediliyor
* Pydantic, JSON objen ne kadar derin (nested) olursa olsun doğrulamasını ve gösterimini yapıyor
* **Genişletilebilir**:
* Pydantic özelleştirilmiş data tiplerinin tanımlanmasının yapılmasına izin veriyor ayrıca validator decoratorü ile senin doğrulamaları genişletip, kendi doğrulayıcılarını yazmana izin veriyor.
* 100% test kapsayıcılığı.

View File

@@ -6,7 +6,7 @@
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
<em>FastAPI framework, yüksek performanslı, öğrenmesi kolay, geliştirmesi hızlı, kullanıma sunulmaya hazır.</em>
</p>
<p align="center">
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
@@ -22,27 +22,27 @@
---
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**dokümantasyon**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
**Kaynak kodu**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
FastAPI, Python 3.6+'nın standart type hintlerine dayanan modern ve hızlı (yüksek performanslı) API'lar oluşturmak için kullanılabilecek web framework'ü.
The key features are:
Ana özellikleri:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Hızlı**: çok yüksek performanslı, **NodeJS** ve **Go** ile eşdeğer seviyede performans sağlıyor, (Starlette ve Pydantic sayesinde.) [Python'un en hızlı frameworklerinden bir tanesi.](#performans).
* **Kodlaması hızlı**: Yeni özellikler geliştirmek neredeyse %200 - %300 daha hızlı. *
* **Daha az bug**: Geliştirici (insan) kaynaklı hatalar neredeyse %40 azaltıldı. *
* **Sezgileri güçlü**: Editor (otomatik-tamamlama) desteği harika. <abbr title="Otomatik tamamlama-IntelliSense">Otomatik tamamlama</abbr> her yerde. Debuglamak ile daha az zaman harcayacaksınız.
* **Kolay**: Öğrenmesi ve kullanması kolay olacak şekilde. Doküman okumak için harcayacağınız süre azaltıldı.
* **Kısa**: Kod tekrarını minimuma indirdik. Fonksiyon parametrelerinin tiplerini belirtmede farklı yollar sunarak karşılaşacağınız bug'ları azalttık.
* **Güçlü**: Otomatik dokümantasyon ile beraber, kullanıma hazır kod yaz.
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
* **Standartlar belirli**: Tamamiyle API'ların açık standartlara bağlı ve (tam uyumlululuk içerisinde); <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (eski adıyla Swagger) ve <a href="http://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
<small>* Bahsi geçen rakamsal ifadeler tamamiyle, geliştirme takımının kendi sundukları ürünü geliştirirken yaptıkları testlere dayanmakta.</small>
## Sponsors
@@ -61,64 +61,72 @@ The key features are:
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
## Opinions
## Görüşler
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
"_[...] Bugünlerde **FastAPI**'ı çok fazla kullanıyorum [...] Aslına bakarsanız **Microsoft'taki Machine Learning servislerimizin** hepsinde kullanmayı düşünüyorum. FastAPI ile geliştirdiğimiz servislerin bazıları çoktan **Windows**'un ana ürünlerine ve **Office** ürünlerine entegre edilmeye başlandı bile._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
"_**FastAPI**'ı **tahminlerimiz**'i sorgulanabilir hale getirmek için **REST** mimarisı ile beraber server üzerinde kullanmaya başladık._"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
"_**Netflix** **kriz yönetiminde** orkestrasyon yapabilmek için geliştirdiği yeni framework'ü **Dispatch**'in, açık kaynak versiyonunu paylaşmaktan gurur duyuyor. [**FastAPI** ile yapıldı.]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
"_**FastAPI** için ayın üzerindeymişcesine heyecanlıyım. Çok eğlenceli!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
"_Dürüst olmak gerekirse, geliştirdiğin şey bir çok açıdan çok sağlam ve parlak gözüküyor. Açıkcası benim **Hug**'ı tasarlarken yapmaya çalıştığım şey buydu - bunu birisinin başardığını görmek gerçekten çok ilham verici._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a>'ın Yaratıcısı</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_Eğer REST API geliştirmek için **modern bir framework** öğrenme arayışında isen, **FastAPI**'a bir göz at [...] Hızlı, kullanımı ve öğrenmesi kolay. [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
"_Biz **API** servislerimizi **FastAPI**'a geçirdik [...] Sizin de beğeneceğinizi düşünüyoruz. [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> kurucuları - <a href="https://spacy.io" target="_blank">spaCy</a> yaratıcıları</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
## **Typer**, komut satırı uygulamalarının FastAPI'ı
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
Eğer API yerine <abbr title="Command Line Interface">komut satırı uygulaması</abbr> geliştiriyor isen <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>'a bir göz at.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
**Typer** kısaca FastAPI'ın küçük kız kardeşi. Komut satırı uygulamalarının **FastAPI'ı** olması hedeflendi. ⌨️ 🚀
## Requirements
## Gereksinimler
Python 3.6+
FastAPI stands on the shoulders of giants:
FastAPI iki devin omuzları üstünde duruyor:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
* Web tarafı için <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a>.
* Data tarafı için <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>.
## Installation
## Yükleme
<div class="termy">
@@ -130,7 +138,7 @@ $ pip install fastapi
</div>
You will also need an ASGI server, for production such as <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
Uygulamanı kullanılabilir hale getirmek için <a href="http://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> ya da <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a> gibi bir ASGI serverına ihtiyacın olacak.
<div class="termy">
@@ -142,11 +150,11 @@ $ pip install uvicorn[standard]
</div>
## Example
## Örnek
### Create it
### Şimdi dene
* Create a file `main.py` with:
* `main.py` adında bir dosya oluştur :
```Python
from typing import Optional
@@ -167,9 +175,9 @@ def read_item(item_id: int, q: Optional[str] = None):
```
<details markdown="1">
<summary>Or use <code>async def</code>...</summary>
<summary>Ya da <code>async def</code>...</summary>
If your code uses `async` / `await`, use `async def`:
Eğer kodunda `async` / `await` var ise, `async def` kullan:
```Python hl_lines="9 14"
from typing import Optional
@@ -189,15 +197,15 @@ async def read_item(item_id: int, q: Optional[str] = None):
return {"item_id": item_id, "q": q}
```
**Note**:
**Not**:
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
Eğer ne olduğunu bilmiyor isen _"Acelen mi var?"_ kısmını oku <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` ve `await`</a>.
</details>
### Run it
### Çalıştır
Run the server with:
Serverı aşağıdaki komut ile çalıştır:
<div class="termy">
@@ -214,54 +222,54 @@ INFO: Application startup complete.
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
<summary>Çalıştırdığımız <code>uvicorn main:app --reload</code> hakkında...</summary>
The command `uvicorn main:app` refers to:
`uvicorn main:app` şunları ifade ediyor:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
* `main`: dosya olan `main.py` (yani Python "modülü").
* `app`: ise `main.py` dosyasının içerisinde oluşturduğumuz `app = FastAPI()` 'a denk geliyor.
* `--reload`: ise kodda herhangi bir değişiklik yaptığımızda serverın yapılan değişiklerileri algılayıp, değişiklikleri siz herhangi bir şey yapmadan uygulamasını sağlıyor.
</details>
### Check it
### Dokümantasyonu kontrol et
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
Browserını aç ve şu linke git <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
You will see the JSON response as:
Bir JSON yanıtı göreceksin:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
Az önce oluşturduğun API:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
* `/` ve `/items/{item_id}` adreslerine HTTP talebi alabilir hale geldi.
* İki _adresde_ `GET` <em>operasyonlarını</em> (HTTP _metodları_ olarakta bilinen) yapabilir hale geldi.
* `/items/{item_id}` _adresi_ ayrıca bir `item_id` _adres parametresine_ sahip ve bu bir `int` olmak zorunda.
* `/items/{item_id}` _adresi_ opsiyonel bir `str` _sorgu paramtersine_ sahip bu da `q`.
### Interactive API docs
### İnteraktif API dokümantasyonu
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Şimdi <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> adresine git.
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
Senin için otomatik oluşturulmuş(<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> tarafından sağlanan) interaktif bir API dokümanı göreceksin:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
### Alternatif API dokümantasyonu
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Şimdi <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> adresine git.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
Senin için alternatif olarak (<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> tarafından sağlanan) bir API dokümantasyonu daha göreceksin:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
## Örnek bir değişiklik
Now modify the file `main.py` to receive a body from a `PUT` request.
Şimdi `main.py` dosyasını değiştirelim ve body ile `PUT` talebi alabilir hale getirelim.
Declare the body using standard Python types, thanks to Pydantic.
Şimdi Pydantic sayesinde, Python'un standart tiplerini kullanarak bir body tanımlayacağız.
```Python hl_lines="4 9 10 11 12 25 26 27"
from typing import Optional
@@ -293,175 +301,175 @@ def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
Server otomatik olarak yeniden başlamalı (çünkü yukarıda `uvicorn`'u çalıştırırken `--reload` parametresini kullandık.).
### Interactive API docs upgrade
### İnteraktif API dokümantasyonu'nda değiştirme yapmak
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Şimdi <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> bağlantısına tekrar git.
* The interactive API documentation will be automatically updated, including the new body:
* İnteraktif API dokümantasyonu, yeni body ile beraber çoktan yenilenmiş olması lazım:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
* "Try it out"a tıkla, bu senin API parametleri üzerinde deneme yapabilmene izin veriyor:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
* Şimdi "Execute" butonuna tıkla, kullanıcı arayüzü otomatik olarak API'ın ile bağlantı kurarak ona bu parametreleri gönderecek ve sonucu karşına getirecek.
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
### Alternatif API dokümantasyonunda değiştirmek
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Şimdi ise <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> adresine git.
* The alternative documentation will also reflect the new query parameter and body:
* Alternatif dokümantasyonda koddaki değişimler ile beraber kendini yeni query ve body ile güncelledi.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
### Özet
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
Özetleyecek olursak, URL, sorgu veya request body'deki parametrelerini fonksiyon parametresi olarak kullanıyorsun. Bu parametrelerin veri tiplerini bir kere belirtmen yeterli.
You do that with standard modern Python types.
Type-hinting işlemini Python dilindeki standart veri tipleri ile yapabilirsin
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Yeni bir syntax'e alışmana gerek yok, metodlar ve classlar zaten spesifik kütüphanelere ait.
Just standard **Python 3.6+**.
Sadece standart **Python 3.6+**.
For example, for an `int`:
Örnek olarak, `int` tanımlamak için:
```Python
item_id: int
```
or for a more complex `Item` model:
ya da daha kompleks `Item` tipi:
```Python
item: Item
```
...and with that single declaration you get:
...sadece kısa bir parametre tipi belirtmekle beraber, sahip olacakların:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* Editör desteği dahil olmak üzere:
* Otomatik tamamlama.
* Tip sorguları.
* Datanın tipe uyumunun sorgulanması:
* Eğer data geçersiz ise, otomatik olarak hataları ayıklar.
* Çok derin JSON objelerinde bile veri tipi sorgusu yapar.
* Gelen verinin <abbr title="parsing, serializing, marshalling olarakta biliniyor">dönüşümünü</abbr> aşağıdaki veri tiplerini kullanarak gerçekleştirebiliyor.
* JSON.
* Path parameters.
* Query parameters.
* Path parametreleri.
* Query parametreleri.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Giden verinin <abbr title="also known as: serialization, parsing, marshalling">dönüşümünü</abbr> aşağıdaki veri tiplerini kullanarak gerçekleştirebiliyor (JSON olarak):
* Python tiplerinin (`str`, `int`, `float`, `bool`, `list`, vs) çevirisi.
* `datetime` objesi.
* `UUID` objesi.
* Veritabanı modelleri.
* ve daha fazlası...
* 2 alternatif kullanıcı arayüzü dahil olmak üzere, otomatik interaktif API dokümanu:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
Az önceki kod örneğine geri dönelim, **FastAPI**'ın yapacaklarına bir bakış atalım:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* 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 `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
* `item_id`'nin `GET` ve `PUT` talepleri içinde olup olmadığının doğruluğunu kontol edecek.
* `item_id`'nin tipinin `int` olduğunu `GET` ve `PUT` talepleri içinde olup olmadığının doğruluğunu kontol edecek.
* Eğer `GET` ve `PUT` içinde yok ise ve `int` değil ise, sebebini belirten bir hata mesajı gösterecek
* Opsiyonel bir `q` parametresinin `GET` talebi için (`http://127.0.0.1:8000/items/foo?q=somequery` içinde) olup olmadığını kontrol edecek
* `q` parametresini `= None` ile oluşturduğumuz için, opsiyonel bir parametre olacak.
* Eğer `None` olmasa zorunlu bir parametre olacak idi (bu yüzden body'de `PUT` parametresi var).
* `PUT` talebi için `/items/{item_id}`'nin body'sini, JSON olarak okuyor:
* `name` adında bir parametetre olup olmadığını ve var ise onun `str` olup olmadığını kontol ediyor.
* `price` adında bir parametetre olup olmadığını ve var ise onun `float` olup olmadığını kontol ediyor.
* `is_offer` adında bir parametetre olup olmadığını ve var ise onun `bool` olup olmadığını kontol ediyor.
* Bunların hepsini en derin JSON modellerinde bile yapacaktır.
* Bütün veri tiplerini otomatik olarak JSON'a çeviriyor veya tam tersi.
* Her şeyi dokümanlayıp, çeşitli yerlerde:
* İnteraktif dokümantasyon sistemleri.
* Otomatik alıcı kodu üretim sistemlerinde ve çeşitli dillerde.
* İki ayrı web arayüzüyle direkt olarak interaktif bir dokümantasyon sunuyor.
---
We just scratched the surface, but you already get the idea of how it all works.
Henüz yüzeysel bir bakış attık, fakat sen çoktan çalışma mantığını anladın.
Try changing the line with:
Şimdi aşağıdaki satırı değiştirmeyi dene:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
...bundan:
```Python
... "item_name": item.name ...
```
...to:
...buna:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
...şimdi editör desteğinin nasıl veri tiplerini bildiğini ve otomatik tamamladığını gör:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
Daha fazla örnek ve özellik için <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a> sayfasını git.
**Spoiler alert**: the tutorial - user guide includes:
**Spoiler**: Öğretici - Kullanıcı rehberi şunları içeriyor:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* 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).
* Many extra features (thanks to Starlette) as:
* **Parameterlerini** nasıl **headers**, **cookies**, **form fields** ve **files** olarak deklare edebileceğini.
* `maximum_length` ya da `regex` gibi şeylerle nasıl **doğrulama** yapabileceğini.
* Çok güçlü ve kullanımı kolay **<abbr title="also known as components, resources, providers, services, injectables">Zorunluluk Entegrasyonu</abbr>** oluşturmayı.
* Güvenlik ve kimlik doğrulama, **JWT tokenleri**'yle beraber **OAuth2** desteği, ve **HTTP Basic** doğrulaması.
* İleri seviye fakat ona göre oldukça basit olan **derince oluşturulmuş JSON modelleri** (Pydantic sayesinde).
* Diğer ekstra özellikler (Starlette sayesinde):
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* `requests` ve `pytest` sayesinde aşırı kolay testler.
* **CORS**
* **Cookie Sessions**
* ...and more.
* ...ve daha fazlası.
## Performance
## Performans
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
Bağımsız TechEmpower kıyaslamaları gösteriyor ki, Uvicorn'la beraber çalışan **FastAPI** uygulamaları <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">Python'un en hızlı frameworklerinden birisi </a>, sadece Starlette ve Uvicorn'dan daha yavaş ki FastAPI bunların üzerine kurulu.
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
Daha fazla bilgi için, bu bölüme bir göz at <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
## Opsiyonel gereksinimler
Used by Pydantic:
Pydantic tarafında kullanılan:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - daha hızlı JSON <abbr title="HTTP bağlantısından gelen stringi Python objesine çevirmek için">"dönüşümü"</abbr> için.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - email doğrulaması için.
Used by Starlette:
Starlette tarafında kullanılan:
* <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`.
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Eğer `TestClient` kullanmak istiyorsan gerekli.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - `FileResponse` ya da `StaticFiles` kullanmak istiyorsan gerekli.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Eğer kendine ait template konfigürasyonu oluşturmak istiyorsan gerekli
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Form kullanmak istiyorsan gerekli <abbr title="HTTP bağlantısından gelen stringi Python objesine çevirmek için">("dönüşümü")</abbr>.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` desteği için gerekli.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - `SchemaGenerator` desteği için gerekli (Muhtemelen FastAPI kullanırken ihtiyacınız olmaz).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - `GraphQLApp` desteği için gerekli.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - `UJSONResponse` kullanmak istiyorsan gerekli.
Used by FastAPI / Starlette:
Hem FastAPI hem de Starlette tarafından kullanılan:
* <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`.
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - oluşturduğumuz uygulamayı bir web sunucusuna servis etmek için gerekli
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse` kullanmak istiyor isen gerekli.
You can install all of these with `pip install fastapi[all]`.
Bunların hepsini `pip install fastapi[all]` ile yükleyebilirsin.
## License
## Lisans
This project is licensed under the terms of the MIT license.
Bu proje, MIT lisansı şartlarına göre lisanslanmıştır.

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -53,12 +54,13 @@ nav:
- tr: /tr/
- uk: /uk/
- zh: /zh/
- features.md
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +69,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +122,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -58,7 +59,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -67,7 +68,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -120,6 +121,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -1,392 +0,0 @@
# 部署
部署 **FastAPI** 应用相对比较简单。
根据特定使用情况和使用工具有几种不同的部署方式。
接下来的章节,你将了解到一些关于部署方式的内容。
## FastAPI 版本
许多应用和系统已经在生产环境使用 **FastAPI**。其测试覆盖率保持在 100%。但该项目仍在快速开发。
我们会经常加入新的功能,定期错误修复,同时也在不断的优化项目代码。
这也是为什么当前版本仍然是 `0.x.x`,我们以此表明每个版本都可能有重大改变。
现在就可以使用 **FastAPI** 创建生产应用(你可能已经使用一段时间了)。你只需要确保使用的版本和代码其他部分能够正常兼容。
### 指定你的 `FastAPI` 版本
你应该做的第一件事情,是为你正在使用的 **FastAPI** 指定一个能够正确运行你的应用的最新版本。
例如,假设你的应用中正在使用版本 `0.45.0`
如果你使用 `requirements.txt` 文件,你可以这样指定版本:
```txt
fastapi==0.45.0
```
这表明你将使用 `0.45.0` 版本的 `FastAPI`
或者你也可以这样指定:
```txt
fastapi>=0.45.0,<0.46.0
```
这表明你将使用 `0.45.0` 及以上,但低于 `0.46.0` 的版本,例如,`0.45.2` 依然可以接受。
如果使用其他工具管理你的安装,比如 PoetryPipenv或者其他工具它们都有各自指定包的版本的方式。
### 可用版本
你可以在 [发行说明](release-notes.md){.internal-link target=_blank} 中查看可用的版本(比如:检查最新版本是什么)。
### 关于版本
FastAPI 遵循语义版本控制约定,`1.0.0` 以下的任何版本都可能加入重大变更。
FastAPI 也遵循这样的约定:任何 ”PATCH“ 版本变更都是用来修复 bug 和向下兼容的变更。
!!! tip
"PATCH" 是指版本号的最后一个数字,例如,在 `0.2.3`PATCH 版本是 `3`
所以,你应该像这样指定版本:
```txt
fastapi>=0.45.0,<0.46.0
```
不兼容变更和新特性在 "MINOR" 版本中添加。
!!! tip
"MINOR" 是版本号中间的数字,例如,在 `0.2.3`MINOR 版本是 `2`
### 更新 FaseAPI 版本
你应该为你的应用添加测试。
使用 **FastAPI** 测试应用非常容易(归功于 Starlette查看文档[测试](tutorial/testing.md){.internal-link target=_blank}
有了测试之后,就可以将 **FastAPI** 更新到最近的一个的版本,然后通过运行测试来确定你所有代码都可以正确工作。
如果一切正常,或者做了必要的修改之后,所有的测试都通过了,就可以把 `FastAPI` 版本指定为那个比较新的版本了。
### 关于 Starlette
不要指定 `starlette` 的版本。
不同版本的 **FastAPI** 会使用特定版本的 Starlette。
所以你只要让 **FastAPI** 自行选择正确的 Starlette 版本。
### 关于 Pydantic
Pydantic 自身的测试中已经包含了 **FastAPI** 的测试,所以最新版本的 Pydantic `1.0.0` 以上版本)总是兼容 **FastAPI**
你可以指定 Pydantic 为任意一个高于 `1.0.0` 且低于的 `2.0.0` 的版本。
例如:
```txt
pydantic>=1.2.0,<2.0.0
```
## Docker
这部分,你将通过指引和链接了解到:
* 如何将你的 **FastAPI** 应用制作成最高性能的 **Docker** 映像/容器。约需五分钟。
* (可选)理解作为一个开发者需要知道的 HTTPS 相关知识。
* 使用自动化 HTTPS 设置一个 Docker Swarm 模式的集群,即使是在一个简单的 $5 USD/month 的服务器上。约需要 20 分钟。
* 使用 Docker Swarm 集群以及 HTTP 等等,生成和部署一个完整的 **FastAPI** 应用。约需 10 分钟。
可以使用 <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> 进行部署。它具有安全性、可复制性、开发简单性等优点。
如果你正在使用 Docker你可以使用官方 Docker 镜像:
### <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
该映像包含一个「自动调优」的机制,这样就可以仅仅添加代码就能自动获得超高性能,而不用做出牺牲。
不过你仍然可以使用环境变量或配置文件更改和更新所有配置。
!!! tip
查看全部配置和选项,请移步 Docker 镜像页面:<a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>。
### 创建 `Dockerfile`
* 进入你的项目目录。
* 使用如下命令创建一个 `Dockerfile`
```Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
COPY ./app /app
```
#### 大型应用
如果遵循创建 [多文件大型应用](tutorial/bigger-applications.md){.internal-link target=_blank} 的章节,你的 Dockerfile 可能看起来是这样:
```Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
COPY ./app /app/app
```
#### 树莓派以及其他架构
如果你在树莓派或者任何其他架构中运行 Docker可以基于 Python 基础镜像(它是多架构的)从头创建一个 `Dockerfile` 并单独使用 Uvicorn。
这种情况下,你的 `Dockerfile` 可能是这样的:
```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"]
```
### 创建 **FastAPI** 代码
* 创建一个 `app` 目录并进入该目录。
* 创建一个 `main.py` 文件,内容如下:
```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}
```
* 现在目录结构如下:
```
.
├── app
│ └── main.py
└── Dockerfile
```
### 构建 Docker 镜像
* 进入项目目录(在 `Dockerfile` 所在的位置,包含 `app` 目录)
* 构建 **FastAPI** 镜像
<div class="termy">
```console
$ docker build -t myimage .
---> 100%
```
</div>
### 启动 Docker 容器
* 运行基于你的镜像容器:
<div class="termy">
```console
$ docker run -d --name mycontainer -p 80:80 myimage
```
</div>
现在你在 Docker 容器中有了一个根据当前服务器和CPU核心的数量自动优化好的 FastAPI 服务器。
### 检查一下
你应该能够在 Docker 容器的 URL 中检查它。例如:<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> 或者 <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> 或者类似的使用Docker主机
得到类似的输出:
```JSON
{"item_id": 5, "q": "somequery"}
```
### 交互式 API 文档
现在可以访问 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 或者 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> 或者类似的使用Docker主机
你会看到一个交互式的 API 文档 (由 <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> 提供):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### 可选的 API 文档
你也可以访问 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 或者 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> 或者类似的使用Docker主机
你将看到一个可选的自动化文档(由 <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 提供)
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## HTTPS
### 关于 HTTPS
我们当然可以假设 HTTPS 只是某种「启用」或「不启用」的东西。
但是事实比这要复杂的多。
!!! tip
如果你着急或者不关心这部分内容,请继续按照下一章节的步骤进行配置。
要从用户的角度学习 HTTPS 的基础,请移步 <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>。
从开发人员的角度来看,在考虑 HTTPS 时有以下几点需要注意:
* 对 HTTPS 来说,服务端需要有第三方生成的「证书」。
* 实际上这些证书是从第三方获取的,而非「生成」的。
* 证书有生命周期。
* 证书会过期。
* 证书过期之后需要更新,重新从第三方获取。
* 连接的加密发生在 TCP 层。
* TCP 层在 HTTP 之下一层。
* 因此,证书和加密处理在 HTTP 之前完成。
* TCP 不知道「域名」,只知道 IP 地址。
* 指定域名的请求信息在 HTTP 的数据中。
* HTTPS 证书「认证」某个特定域名,但是协议和加密在知道要处理哪个域名之前就已经在 TCP 层发生了。
* 默认情况下,一个 IP 地址仅有一个 HTTPS 证书。
* 无论你的服务器大小,都是如此。
* 但是对此有解决办法。
* TSL 协议(在 TCP 层处理加密的协议,发生在 HTTP 之前)有一个扩展,叫 <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>。
* SNI 扩展允许一个服务器(一个 IP 地址)有多个 HTTPS 证书,为多个 HTTPS 域名/应用 提供服务。
* 要使其工作,服务器运行的单一组件(程序)监听公网 IP 地址,所有 HTTPS 证书必须都在该服务器上。
* 在获得一个安全连接之后,通讯协议仍然是 HTTP。
* HTTP 内容是加密的,即使这些内容使用 HTTP 协议传输。
常见的做法是在服务器(机器,主机等等)上运行一个程序或 HTTP 服务来管理所有的 HTTPS 部分:将解密后的 HTTP 请求发送给在同一服务器运行的真实 HTTP 应用(在这里是 **FastAPI** 应用),从应用获得 HTTP 响应,使用适当的证书加密响应然后使用 HTTPS 将其发回客户端。这个服务器常被称作 <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS 终止代理</a>。
### Let's Encrypt
在 Let's Encrypt 出现之前,这些 HTTPS 证书由受信任的第三方出售。
获取这些证书的过程曾经非常繁琐,需要大量的文书工作,而且证书的价格也相当昂贵。
但是紧接着 <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> 被创造了。
这是一个来自 Linux 基金会的项目。它以自动化的方式免费提供 HTTPS 证书。这些证书使用所有的标准加密措施,且证书生命周期很短(大约 3 个月),正是由于它们生命周期的减短,所以实际上安全性更高。
对域名进行安全验证并自动生成证书。同时也允许自动更新这些证书。
其想法是自动获取和更新这些证书,这样就可以一直免费获得安全的 HTTPS。
### Traefik
<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a> 是一个高性能的反向代理/负载均衡器。它能够完成「TLS 终止代理」的工作(其他特性除外)。
Traefik 集成了 Let's Encrypt所以能够处理全部 HTTPS 的部分,包括证书获取与更新。
Traefik 也集成了 Docker所以你也可以在每个应用的配置中声明你的域名并可以让它读取这些配置生成 HTTPS 证书并自动将 HTTPS 提供给你的应用程序,而你不需要对其配置进行任何更改。
---
有了这些信息和工具,就可以进入下一节把所有内容结合到一起。
## 通过 Traefik 和 HTTPS 搭建 Docker Swarm mode 集群
通过一个主要的 Traefik 来处理 HTTPS (包括证书获取和更新),大约 20 分钟就可以搭建好一个 Docker Swarm mode 集群。
借助 Docker Swarm mode你可以从单个机器的集群开始甚至可以是 $5 /月的服务器),然后你可以根据需要添加更多的服务器来进行扩展。
要使用 Traefik 和 HTTPS 处理来构建 Docker Swarm Mode 集群,请遵循以下指南:
### <a href="https://medium.com/@tiangolo/docker-swarm-mode-and-traefik-for-a-https-cluster-20328dba6232" class="external-link" target="_blank">Docker Swarm Mode 和 Traefik 用于 HTTPS 集群</a>
### 部署一个 FastAPI 应用
部署的最简单方式就是使用 [**FastAPI** 项目生成器](project-generation.md){.internal-link target=_blank}。
它被设计成与上述带有 Traefik 和 HTTPS 的 Docker Swarm 集群整合到一起。
你可以在大概两分钟内生成一个项目。
生成的项目有部署说明,需要再花两分钟部署项目。
## 或者,不用 Docker 部署 **FastAPI**
你也可以不用 Docker 直接部署 **FastAPI**
只需要安装一个兼容 ASGI 的服务器:
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>,一个轻量快速的 ASGI 服务器,基于 uvloop 和 httptools 构建。
<div class="termy">
```console
$ pip install uvicorn[standard]
---> 100%
```
</div>
* <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>,一个也兼容 HTTP/2 的 ASGI 服务器。
<div class="termy">
```console
$ pip install hypercorn
---> 100%
```
</div>
...或者任何其他的 ASGI 服务器。
然后使用教程中同样的方式来运行你的应用,但是不要加 `--reload` 选项,比如:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
或者使用 Hypercorn
<div class="termy">
```console
$ hypercorn main:app --bind 0.0.0.0:80
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
```
</div>
也许你想编写一些工具来确保它停止时会自动重启。
或者安装 <a href="https://gunicorn.org/" class="external-link" target="_blank">Gunicorn</a> 并 <a href="https://www.uvicorn.org/#running-with-gunicorn" class="external-link" target="_blank">将其作为 Uvicorn 的管理器</a>或者使用多职程worker的 Hypercorn。
或者保证精确调整职程的数量等等。
但是如果你正做这些,你可能只需要使用 Docker 镜像就能够自动做到这些了。

View File

@@ -114,25 +114,28 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋
他们主要通过<a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>支持我在 **FastAPI** (和其他项目)的工作。
{% if sponsors.gold %}
### 金牌赞助商
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.silver %}
### 银牌赞助商
{% if sponsors %}
{% for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### 铜牌赞助商
{% if sponsors %}
{% for sponsor in sponsors.bronze -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
@@ -140,28 +143,22 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋
### 个人赞助
{% if people %}
{% if people.sponsors_50 %}
{% if github_sponsors %}
{% for group in github_sponsors.sponsors %}
<div class="user-list user-list-center">
{% for user in people.sponsors_50 %}
{% for user in group %}
{% if user.login not in sponsors_badge.logins %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if people %}
<div class="user-list user-list-center">
{% for user in people.sponsors %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div>
{% endfor %}
</div>
{% endif %}
## 关于数据 - 技术细节

View File

@@ -33,7 +33,6 @@
编写带有类型标注的标准 Python
```Python
from typing import List, Dict
from datetime import date
from pydantic import BaseModel

View File

@@ -0,0 +1,73 @@
# 路径操作装饰器依赖项
有时,我们并不需要在*路径操作函数*中使用依赖项的返回值。
或者说,有些依赖项不返回值。
但仍要执行或解析该依赖项。
对于这种情况,不必在声明*路径操作函数*的参数时使用 `Depends`,而是可以在*路径操作装饰器*中添加一个由 `dependencies` 组成的 `list`
## 在*路径操作装饰器*中添加 `dependencies` 参数
*路径操作装饰器*支持可选参数 ~ `dependencies`
该参数的值是由 `Depends()` 组成的 `list`
```Python hl_lines="17"
{!../../../docs_src/dependencies/tutorial006.py!}
```
路径操作装饰器依赖项(以下简称为**“路径装饰器依赖项”**)的执行或解析方式和普通依赖项一样,但就算这些依赖项会返回值,它们的值也不会传递给*路径操作函数*。
!!! tip "提示"
有些编辑器会检查代码中没使用过的函数参数,并显示错误提示。
在*路径操作装饰器*中使用 `dependencies` 参数,可以确保在执行依赖项的同时,避免编辑器显示错误提示。
使用路径装饰器依赖项还可以避免开发新人误会代码中包含无用的未使用参数。
!!! info "说明"
本例中,使用的是自定义响应头 `X-Key` 和 `X-Token`。
但实际开发中,尤其是在实现安全措施时,最好使用 FastAPI 内置的[安全工具](../security/index.md){.internal-link target=_blank}(详见下一章)。
## 依赖项错误和返回值
路径装饰器依赖项也可以使用普通的依赖项*函数*。
### 依赖项的需求项
路径装饰器依赖项可以声明请求的需求项(比如响应头)或其他子依赖项:
```Python hl_lines="6 11"
{!../../../docs_src/dependencies/tutorial006.py!}
```
### 触发异常
路径装饰器依赖项与正常的依赖项一样,可以 `raise` 异常:
```Python hl_lines="8 13"
{!../../../docs_src/dependencies/tutorial006.py!}
```
### 返回值
无论路径装饰器依赖项是否返回值,路径操作都不会使用这些值。
因此,可以复用在其他位置使用过的、(能返回值的)普通依赖项,即使没有使用这个值,也会执行该依赖项:
```Python hl_lines="9 14"
{!../../../docs_src/dependencies/tutorial006.py!}
```
## 为一组路径操作定义依赖项
稍后,[大型应用 - 多文件](../../tutorial/bigger-applications.md){.internal-link target=\_blank}一章中会介绍如何使用多个文件创建大型应用程序,在这一章中,您将了解到如何为一组*路径操作*声明单个 `dependencies` 参数。
## 全局依赖项
接下来,我们将学习如何为 `FastAPI` 应用程序添加全局依赖项,创建应用于每个*路径操作*的依赖项。

View File

@@ -0,0 +1,88 @@
# 子依赖项
FastAPI 支持创建含**子依赖项**的依赖项。
并且,可以按需声明任意**深度**的子依赖项嵌套层级。
**FastAPI** 负责处理解析不同深度的子依赖项。
### 第一层依赖项
下列代码创建了第一层依赖项:
```Python hl_lines="8-9"
{!../../../docs_src/dependencies/tutorial005.py!}
```
这段代码声明了类型为 `str` 的可选查询参数 `q`,然后返回这个查询参数。
这个函数很简单(不过也没什么用),但却有助于让我们专注于了解子依赖项的工作方式。
### 第二层依赖项
接下来,创建另一个依赖项函数,并同时用该依赖项自身再声明一个依赖项(所以这也是一个「依赖项」):
```Python hl_lines="13"
{!../../../docs_src/dependencies/tutorial005.py!}
```
这里重点说明一下声明的参数:
* 尽管该函数自身是依赖项,但还声明了另一个依赖项(它「依赖」于其他对象)
* 该函数依赖 `query_extractor`, 并把 `query_extractor` 的返回值赋给参数 `q`
* 同时,该函数还声明了类型是 `str` 的可选 cookie`last_query`
* 用户未提供查询参数 `q` 时,则使用上次使用后保存在 cookie 中的查询
### 使用依赖项
接下来,就可以使用依赖项:
```Python hl_lines="21"
{!../../../docs_src/dependencies/tutorial005.py!}
```
!!! info "信息"
注意,这里在*路径操作函数*中只声明了一个依赖项,即 `query_or_cookie_extractor` 。
但 **FastAPI** 必须先处理 `query_extractor`,以便在调用 `query_or_cookie_extractor` 时使用 `query_extractor` 返回的结果。
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## 多次使用同一个依赖项
如果在同一个*路径操作* 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,**FastAPI** 在处理同一请求时,只调用一次该子依赖项。
FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行<abbr title="一个实用程序/系统来存储计算/生成的值,以便重用它们,而不是再次计算它们。">「缓存」</abbr>,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。
在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 `Depends` 的参数 `use_cache` 的值设置为 `False` :
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
## 小结
千万别被本章里这些花里胡哨的词藻吓倒了,其实**依赖注入**系统非常简单。
依赖注入无非是与*路径操作函数*一样的函数罢了。
但它依然非常强大,能够声明任意嵌套深度的「图」或树状的依赖结构。
!!! tip "提示"
这些简单的例子现在看上去虽然没有什么实用价值,
但在**安全**一章中,您会了解到这些例子的用途,
以及这些例子所能节省的代码量。

View File

@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -82,6 +83,8 @@ nav:
- tutorial/body-updates.md
- 依赖项:
- tutorial/dependencies/index.md
- tutorial/dependencies/sub-dependencies.md
- tutorial/dependencies/dependencies-in-path-operation-decorators.md
- tutorial/dependencies/global-dependencies.md
- 安全性:
- tutorial/security/index.md
@@ -98,7 +101,6 @@ nav:
- advanced/additional-status-codes.md
- advanced/response-directly.md
- advanced/custom-response.md
- deployment.md
- contributing.md
- help-fastapi.md
- benchmarks.md
@@ -107,7 +109,7 @@ markdown_extensions:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
- mdx_include:
base_path: docs
- admonition
- codehilite
@@ -116,7 +118,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed
extra:
social:
@@ -169,6 +171,5 @@ extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

View File

@@ -1,14 +1,26 @@
import graphene
import strawberry
from fastapi import FastAPI
from starlette.graphql import GraphQLApp
from strawberry.asgi import GraphQL
class Query(graphene.ObjectType):
hello = graphene.String(name=graphene.String(default_value="stranger"))
@strawberry.type
class User:
name: str
age: int
def resolve_hello(self, info, name):
return "Hello " + name
@strawberry.type
class Query:
@strawberry.field
def user(self) -> User:
return User(name="Patrick", age=100)
schema = strawberry.Schema(query=Query)
graphql_app = GraphQL(schema)
app = FastAPI()
app.add_route("/", GraphQLApp(schema=graphene.Schema(query=Query)))
app.add_route("/graphql", graphql_app)
app.add_websocket_route("/graphql", graphql_app)

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.68.0"
__version__ = "0.68.2"
from starlette import status as status

View File

@@ -65,6 +65,13 @@ def _prepare_response_content(
exclude_none: bool = False,
) -> Any:
if isinstance(res, BaseModel):
read_with_orm_mode = getattr(res.__config__, "read_with_orm_mode", None)
if read_with_orm_mode:
# Let from_orm extract the data from this model instead of converting
# it now to a dict.
# Otherwise there's no way to extract lazy data that requires attribute
# access instead of dict iteration, e.g. lazy relationships.
return res
return res.dict(
by_alias=True,
exclude_unset=exclude_unset,

View File

@@ -1,25 +0,0 @@
[mypy]
# --strict
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_return_any = True
implicit_reexport = False
strict_equality = True
# --strict end
[mypy-fastapi.concurrency]
warn_unused_ignores = False
ignore_missing_imports = True
[mypy-fastapi.tests.*]
ignore_missing_imports = True
check_untyped_defs = True

View File

@@ -28,6 +28,7 @@ classifiers = [
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
"Topic :: Internet :: WWW/HTTP",
]
@@ -36,7 +37,7 @@ requires = [
"pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0"
]
description-file = "README.md"
requires-python = ">=3.6"
requires-python = ">=3.6.1"
[tool.flit.metadata.urls]
Documentation = "https://fastapi.tiangolo.com/"
@@ -44,62 +45,99 @@ Documentation = "https://fastapi.tiangolo.com/"
[tool.flit.metadata.requires-extra]
test = [
"pytest >=6.2.4,<7.0.0",
"pytest-cov >=2.12.0,<3.0.0",
"pytest-asyncio >=0.14.0,<0.15.0",
"mypy ==0.812",
"pytest-cov >=2.12.0,<4.0.0",
"pytest-asyncio >=0.14.0,<0.16.0",
"mypy ==0.910",
"flake8 >=3.8.3,<4.0.0",
"black ==20.8b1",
"black ==21.9b0",
"isort >=5.0.6,<6.0.0",
"requests >=2.24.0,<3.0.0",
"httpx >=0.14.0,<0.15.0",
"httpx >=0.14.0,<0.19.0",
"email_validator >=1.1.1,<2.0.0",
"sqlalchemy >=1.3.18,<1.4.0",
"sqlalchemy >=1.3.18,<1.5.0",
"peewee >=3.13.3,<4.0.0",
"databases[sqlite] >=0.3.2,<0.4.0",
"databases[sqlite] >=0.3.2,<0.6.0",
"orjson >=3.2.1,<4.0.0",
"ujson >=4.0.1,<5.0.0",
"async_exit_stack >=1.0.1,<2.0.0",
"async_generator >=1.10,<2.0.0",
"python-multipart >=0.0.5,<0.0.6",
"aiofiles >=0.5.0,<0.6.0",
"flask >=1.1.2,<2.0.0"
"aiofiles >=0.5.0,<0.8.0",
# TODO: try to upgrade after upgrading Starlette
"flask >=1.1.2,<2.0.0",
"async_exit_stack >=1.0.1,<2.0.0; python_version < '3.7'",
"async_generator >=1.10,<2.0.0; python_version < '3.7'",
# types
"types-ujson ==0.1.1",
"types-orjson ==3.6.0",
"types-dataclasses ==0.1.7; python_version<'3.7'",
]
doc = [
"mkdocs >=1.1.2,<2.0.0",
"mkdocs-material >=7.1.9,<8.0.0",
"markdown-include >=0.6.0,<0.7.0",
"mkdocs-markdownextradata-plugin >=0.1.7,<0.2.0",
"mdx-include >=1.4.1,<2.0.0",
"mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0",
"typer-cli >=0.0.12,<0.0.13",
"pyyaml >=5.3.1,<6.0.0"
]
dev = [
"python-jose[cryptography] >=3.3.0,<4.0.0",
"passlib[bcrypt] >=1.7.2,<2.0.0",
"autoflake >=1.3.1,<2.0.0",
"autoflake >=1.4.0,<2.0.0",
"flake8 >=3.8.3,<4.0.0",
"uvicorn[standard] >=0.12.0,<0.14.0",
"uvicorn[standard] >=0.12.0,<0.16.0",
# TODO: remove in the next major version
"graphene >=2.1.8,<3.0.0"
]
all = [
"requests >=2.24.0,<3.0.0",
"aiofiles >=0.5.0,<0.6.0",
"aiofiles >=0.5.0,<0.8.0",
# TODO: try to upgrade after upgrading Starlette
"jinja2 >=2.11.2,<3.0.0",
"python-multipart >=0.0.5,<0.0.6",
# TODO: try to upgrade after upgrading Starlette
"itsdangerous >=1.1.0,<2.0.0",
"pyyaml >=5.3.1,<6.0.0",
# TODO: remove in the next major version
"graphene >=2.1.8,<3.0.0",
"ujson >=4.0.1,<5.0.0",
"orjson >=3.2.1,<4.0.0",
"email_validator >=1.1.1,<2.0.0",
"uvicorn[standard] >=0.12.0,<0.14.0",
"async_exit_stack >=1.0.1,<2.0.0",
"async_generator >=1.10,<2.0.0"
"uvicorn[standard] >=0.12.0,<0.16.0",
"async_exit_stack >=1.0.1,<2.0.0; python_version < '3.7'",
"async_generator >=1.10,<2.0.0; python_version < '3.7'",
]
[tool.isort]
profile = "black"
known_third_party = ["fastapi", "pydantic", "starlette"]
[tool.mypy]
# --strict
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
implicit_reexport = false
strict_equality = true
# --strict end
[[tool.mypy.overrides]]
module = "fastapi.concurrency"
warn_unused_ignores = false
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "fastapi.tests.*"
ignore_missing_imports = true
check_untyped_defs = true
[tool.pytest.ini_options]
addopts = [
"--strict-config",

View File

@@ -1,6 +0,0 @@
#!/bin/sh -e
set -x
# Sort imports one per line, so autoflake can remove unused imports
isort fastapi tests docs_src scripts --force-single-line-imports
sh ./scripts/format.sh

View File

@@ -0,0 +1,53 @@
from typing import Any
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
class PersonBase(BaseModel):
name: str
lastname: str
class Person(PersonBase):
@property
def full_name(self) -> str:
return f"{self.name} {self.lastname}"
class Config:
orm_mode = True
read_with_orm_mode = True
class PersonCreate(PersonBase):
pass
class PersonRead(PersonBase):
full_name: str
class Config:
orm_mode = True
app = FastAPI()
@app.post("/people/", response_model=PersonRead)
def create_person(person: PersonCreate) -> Any:
db_person = Person.from_orm(person)
return db_person
client = TestClient(app)
def test_read_with_orm_mode() -> None:
person_data = {"name": "Dive", "lastname": "Wilson"}
response = client.post("/people/", json=person_data)
data = response.json()
assert response.status_code == 200, response.text
assert data["name"] == person_data["name"]
assert data["lastname"] == person_data["lastname"]
assert data["full_name"] == person_data["name"] + " " + person_data["lastname"]

View File

@@ -5,7 +5,7 @@ from unittest.mock import MagicMock
import pytest
from fastapi.testclient import TestClient
from ...utils import skip_py36
from ...utils import needs_py37
openapi_schema = {
"openapi": "3.0.2",
@@ -340,14 +340,14 @@ def client():
test_db.unlink()
@skip_py36
@needs_py37
def test_openapi_schema(client):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
@skip_py36
@needs_py37
def test_create_user(client):
test_user = {"email": "johndoe@example.com", "password": "secret"}
response = client.post("/users/", json=test_user)
@@ -359,7 +359,7 @@ def test_create_user(client):
assert response.status_code == 400, response.text
@skip_py36
@needs_py37
def test_get_user(client):
response = client.get("/users/1")
assert response.status_code == 200, response.text
@@ -368,13 +368,13 @@ def test_get_user(client):
assert "id" in data
@skip_py36
@needs_py37
def test_inexistent_user(client):
response = client.get("/users/999")
assert response.status_code == 404, response.text
@skip_py36
@needs_py37
def test_get_users(client):
response = client.get("/users/")
assert response.status_code == 200, response.text
@@ -386,7 +386,7 @@ def test_get_users(client):
time.sleep = MagicMock()
@skip_py36
@needs_py37
def test_get_slowusers(client):
response = client.get("/slowusers/")
assert response.status_code == 200, response.text
@@ -395,7 +395,7 @@ def test_get_slowusers(client):
assert "id" in data[0]
@skip_py36
@needs_py37
def test_create_item(client):
item = {"title": "Foo", "description": "Something that fights"}
response = client.post("/users/1/items/", json=item)
@@ -419,7 +419,7 @@ def test_create_item(client):
assert item_to_check["description"] == item["description"]
@skip_py36
@needs_py37
def test_read_items(client):
response = client.get("/items/")
assert response.status_code == 200, response.text

Some files were not shown because too many files have changed in this diff Show More