Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
378fa4ef75 | ||
|
|
eb515b1af7 | ||
|
|
19fd336cde | ||
|
|
63744b2e8a | ||
|
|
85f0d2b924 | ||
|
|
507e9baf9e | ||
|
|
c6461ad7dc | ||
|
|
5fbf597cd5 | ||
|
|
bc99d2b7b8 | ||
|
|
0eb27ab4d0 | ||
|
|
68c43eb126 | ||
|
|
00ac07f65c | ||
|
|
d8b0a9dc6e | ||
|
|
8c36572d23 | ||
|
|
e0e3224588 | ||
|
|
4e2595f928 | ||
|
|
6de8748788 | ||
|
|
39ec5b33d9 | ||
|
|
557fe61d92 | ||
|
|
458a7fbf15 | ||
|
|
ae22bca9fe | ||
|
|
9229ba8078 | ||
|
|
96ca425b1f | ||
|
|
c86a4e1dd3 | ||
|
|
24749aef71 | ||
|
|
f6a3bc2902 | ||
|
|
ba8c78d87f | ||
|
|
69784e5141 | ||
|
|
8a02a47124 | ||
|
|
5607c198c4 | ||
|
|
cf5e67590a | ||
|
|
655db2af1f | ||
|
|
8c102814fd | ||
|
|
189ac3e280 | ||
|
|
2680f369d0 | ||
|
|
284eb6615d | ||
|
|
099c478655 | ||
|
|
54c02d402a | ||
|
|
4b968c4e39 | ||
|
|
1db0fc2953 | ||
|
|
d29aa3d436 | ||
|
|
1b6350ad9e | ||
|
|
cb7e79ab8a | ||
|
|
fe086a4903 | ||
|
|
64e7deaebc | ||
|
|
bee35f5ae1 | ||
|
|
3a786a7a3a | ||
|
|
eaeafc32c4 | ||
|
|
c8b4e4d455 | ||
|
|
1760da0efa | ||
|
|
f49ba24b71 | ||
|
|
7b6e198d31 | ||
|
|
6ac35b1c2a | ||
|
|
dc9c570733 | ||
|
|
20d4834546 | ||
|
|
f7d7c6c865 | ||
|
|
c03b9b2d4c | ||
|
|
70f1b8ebd0 | ||
|
|
13bf477579 | ||
|
|
ab5afd0b42 | ||
|
|
b5dcd98605 | ||
|
|
2280a48c67 | ||
|
|
8c5d1f30dc | ||
|
|
98a0fe8628 | ||
|
|
38c52fa12d | ||
|
|
7c2ed25d6f | ||
|
|
dda105c50d | ||
|
|
326837443f | ||
|
|
cb5b4e1b7e | ||
|
|
a23eaaeda8 | ||
|
|
d0ced2c7c9 | ||
|
|
3409c9e32f | ||
|
|
53a86a885d | ||
|
|
4ac310875d | ||
|
|
30e742d566 | ||
|
|
84490a2160 | ||
|
|
4d4452ede5 | ||
|
|
25a48287ed | ||
|
|
9a33a0e24c | ||
|
|
27fd954a45 | ||
|
|
ab33ba27af | ||
|
|
e9ba01dc59 | ||
|
|
e809bdc170 | ||
|
|
717a1ec409 | ||
|
|
eba2c66532 | ||
|
|
3788ca4640 | ||
|
|
97fa743ecb | ||
|
|
e726b7b771 | ||
|
|
6a22b75afd | ||
|
|
4093d9b22a | ||
|
|
18daf8f301 | ||
|
|
962578bec2 | ||
|
|
85cff59109 | ||
|
|
b869c7c872 | ||
|
|
016c3f0d5b | ||
|
|
4eb9c60652 | ||
|
|
8879757a88 | ||
|
|
fadfcfda4a | ||
|
|
b8c4149e89 | ||
|
|
836bb97a2d | ||
|
|
7db359182d | ||
|
|
4eada92883 | ||
|
|
6a74f3a0c1 | ||
|
|
daa0765653 | ||
|
|
27e26b5939 | ||
|
|
6f45f43709 | ||
|
|
6c80e9a8e0 | ||
|
|
3b2e891917 | ||
|
|
9121fccf55 | ||
|
|
fa2c750443 | ||
|
|
29e7b31ee6 | ||
|
|
bb6c6ed5d1 | ||
|
|
1a5273773e | ||
|
|
3f40c37905 | ||
|
|
996dfd05bd | ||
|
|
0a1dd7894c | ||
|
|
dbfd3f7e18 |
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security Contact
|
||||
about: Please report security vulnerabilities to security@tiangolo.com
|
||||
|
||||
181
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea or ask for a feature that you would like to have in FastAPI
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for your interest in FastAPI! 🚀
|
||||
|
||||
Please follow these instructions, fill every question, and do every step. 🙏
|
||||
|
||||
I'm asking this because answering questions and solving problems in GitHub issues is what consumes most of the time.
|
||||
|
||||
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling issues.
|
||||
|
||||
All that, on top of all the incredible help provided by a bunch of community members, the [FastAPI Experts](https://fastapi.tiangolo.com/fastapi-people/#experts), that give a lot of their time to come here and help others.
|
||||
|
||||
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
||||
|
||||
By asking questions in a structured way (following this) it will be much easier to help you.
|
||||
|
||||
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
|
||||
|
||||
As there are too many issues with questions, I'll have to close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: First Check
|
||||
description: Please confirm and check all the following options.
|
||||
options:
|
||||
- label: I added a very descriptive title to this issue.
|
||||
required: true
|
||||
- label: I used the GitHub search to find a similar issue and didn't find it.
|
||||
required: true
|
||||
- label: I searched the FastAPI documentation, with the integrated search.
|
||||
required: true
|
||||
- label: I already searched in Google "How to X in FastAPI" and didn't find any information.
|
||||
required: true
|
||||
- label: I already read and followed all the tutorial in the docs and didn't find an answer.
|
||||
required: true
|
||||
- label: I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
|
||||
required: true
|
||||
- label: I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
required: true
|
||||
- label: I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: help
|
||||
attributes:
|
||||
label: Commit to Help
|
||||
description: |
|
||||
After submitting this, I commit to one of:
|
||||
|
||||
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
||||
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
||||
* Implement a Pull Request for a confirmed bug.
|
||||
|
||||
options:
|
||||
- label: I commit to help with one of those options 👆
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: Example Code
|
||||
description: |
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
|
||||
|
||||
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
|
||||
|
||||
placeholder: |
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
render: python
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
What is your feature request?
|
||||
|
||||
Write a short description telling me what you are trying to solve and what you are currently doing.
|
||||
placeholder: |
|
||||
* Open the browser and call the endpoint `/`.
|
||||
* It returns a JSON with `{"Hello": "World"}`.
|
||||
* I would like it to have an extra parameter to teleport me to the moon and back.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: wanted-solution
|
||||
attributes:
|
||||
label: Wanted Solution
|
||||
description: |
|
||||
Tell me what's the solution you would like.
|
||||
placeholder: |
|
||||
I would like it to have a `teleport_to_moon` parameter that defaults to `False`, and can be set to `True` to teleport me.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: wanted-code
|
||||
attributes:
|
||||
label: Wanted Code
|
||||
description: Show me an example of how you would want the code to look like.
|
||||
placeholder: |
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/", teleport_to_moon=True)
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
render: python
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: |
|
||||
Tell me about alternatives you've considered.
|
||||
placeholder: |
|
||||
To wait for Space X moon travel plans to drop down long after they release them. But I would rather teleport.
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: What operating system are you on?
|
||||
multiple: true
|
||||
options:
|
||||
- Linux
|
||||
- Windows
|
||||
- macOS
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: os-details
|
||||
attributes:
|
||||
label: Operating System Details
|
||||
description: You can add more details about your operating system here, in particular if you chose "Other".
|
||||
- type: input
|
||||
id: fastapi-version
|
||||
attributes:
|
||||
label: FastAPI Version
|
||||
description: |
|
||||
What FastAPI version are you using?
|
||||
|
||||
You can find the FastAPI version with:
|
||||
|
||||
```bash
|
||||
python -c "import fastapi; print(fastapi.__version__)"
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: python-version
|
||||
attributes:
|
||||
label: Python Version
|
||||
description: |
|
||||
What Python version are you using?
|
||||
|
||||
You can find the Python version with:
|
||||
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any additional context information or screenshots you think are useful.
|
||||
104
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,104 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ""
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### First check
|
||||
|
||||
* [ ] I added a very descriptive title to this issue.
|
||||
* [ ] I used the GitHub search to find a similar issue and didn't find it.
|
||||
* [ ] I searched the FastAPI documentation, with the integrated search.
|
||||
* [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
|
||||
* [ ] I already read and followed all the tutorial in the docs and didn't find an answer.
|
||||
* [ ] I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
|
||||
* [ ] I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
* [ ] I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
|
||||
* [ ] After submitting this, I commit to:
|
||||
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
||||
* Or, I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
||||
* Implement a Pull Request for a confirmed bug.
|
||||
|
||||
<!--
|
||||
|
||||
I'm asking all this because answering questions and solving problems in GitHub issues consumes a lot of time. I end up not being able to add new features, fix bugs, review Pull Requests, etc. as fast as I wish because I have to spend too much time handling issues.
|
||||
|
||||
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
|
||||
|
||||
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
||||
|
||||
-->
|
||||
|
||||
### Example
|
||||
|
||||
Here's a self-contained [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with my use case:
|
||||
|
||||
<!-- Replace the code below with your own self-contained, minimal, reproducible, example -->
|
||||
|
||||
```Python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
<!-- Replace the content below with your own feature request -->
|
||||
|
||||
* Open the browser and call the endpoint `/`.
|
||||
* It returns a JSON with `{"Hello": "World"}`.
|
||||
* I would like it to have an extra parameter to teleport me to the moon and back.
|
||||
|
||||
### The solution you would like
|
||||
|
||||
<!-- Replace this with your own content -->
|
||||
|
||||
I would like it to have a `teleport_to_moon` parameter that defaults to `False`, and can be set to `True` to teleport me:
|
||||
|
||||
```Python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/", teleport_to_moon=True)
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
```
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
<!-- Replace this with your own ideas -->
|
||||
|
||||
To wait for Space X moon travel plans to drop down long after they release them. But I would rather teleport.
|
||||
|
||||
### Environment
|
||||
|
||||
* OS: [e.g. Linux / Windows / macOS]:
|
||||
* FastAPI Version [e.g. 0.3.0]:
|
||||
|
||||
To know the FastAPI version use:
|
||||
|
||||
```bash
|
||||
python -c "import fastapi; print(fastapi.__version__)"
|
||||
```
|
||||
|
||||
* Python version:
|
||||
|
||||
To know the Python version use:
|
||||
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the question here. -->
|
||||
81
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,81 +0,0 @@
|
||||
---
|
||||
name: Question or Problem
|
||||
about: Ask a question or ask about a problem
|
||||
title: ""
|
||||
labels: question
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
|
||||
### First check
|
||||
|
||||
* [ ] I added a very descriptive title to this issue.
|
||||
* [ ] I used the GitHub search to find a similar issue and didn't find it.
|
||||
* [ ] I searched the FastAPI documentation, with the integrated search.
|
||||
* [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
|
||||
* [ ] I already read and followed all the tutorial in the docs and didn't find an answer.
|
||||
* [ ] I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
|
||||
* [ ] I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
* [ ] I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
|
||||
* [ ] After submitting this, I commit to one of:
|
||||
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
||||
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
||||
* Implement a Pull Request for a confirmed bug.
|
||||
|
||||
<!--
|
||||
|
||||
I'm asking all this because answering questions and solving problems in GitHub issues consumes a lot of time. I end up not being able to add new features, fix bugs, review Pull Requests, etc. as fast as I wish because I have to spend too much time handling issues.
|
||||
|
||||
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
|
||||
|
||||
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
||||
|
||||
-->
|
||||
|
||||
### Example
|
||||
|
||||
Here's a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with my use case:
|
||||
|
||||
<!-- Replace the code below with your own self-contained, minimal, reproducible, example, if I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you -->
|
||||
|
||||
```Python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
<!-- Replace the content below with your own problem, question, or error -->
|
||||
|
||||
* Open the browser and call the endpoint `/`.
|
||||
* It returns a JSON with `{"Hello": "World"}`.
|
||||
* But I expected it to return `{"Hello": "Sara"}`.
|
||||
|
||||
### Environment
|
||||
|
||||
* OS: [e.g. Linux / Windows / macOS]:
|
||||
* FastAPI Version [e.g. 0.3.0]:
|
||||
|
||||
To know the FastAPI version use:
|
||||
|
||||
```bash
|
||||
python -c "import fastapi; print(fastapi.__version__)"
|
||||
```
|
||||
|
||||
* Python version:
|
||||
|
||||
To know the Python version use:
|
||||
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the question here. -->
|
||||
146
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
name: Question or Problem
|
||||
description: Ask a question or ask about a problem
|
||||
labels: [question]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for your interest in FastAPI! 🚀
|
||||
|
||||
Please follow these instructions, fill every question, and do every step. 🙏
|
||||
|
||||
I'm asking this because answering questions and solving problems in GitHub issues is what consumes most of the time.
|
||||
|
||||
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling issues.
|
||||
|
||||
All that, on top of all the incredible help provided by a bunch of community members, the [FastAPI Experts](https://fastapi.tiangolo.com/fastapi-people/#experts), that give a lot of their time to come here and help others.
|
||||
|
||||
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
||||
|
||||
By asking questions in a structured way (following this) it will be much easier to help you.
|
||||
|
||||
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
|
||||
|
||||
As there are too many issues with questions, I'll have to close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: First Check
|
||||
description: Please confirm and check all the following options.
|
||||
options:
|
||||
- label: I added a very descriptive title to this issue.
|
||||
required: true
|
||||
- label: I used the GitHub search to find a similar issue and didn't find it.
|
||||
required: true
|
||||
- label: I searched the FastAPI documentation, with the integrated search.
|
||||
required: true
|
||||
- label: I already searched in Google "How to X in FastAPI" and didn't find any information.
|
||||
required: true
|
||||
- label: I already read and followed all the tutorial in the docs and didn't find an answer.
|
||||
required: true
|
||||
- label: I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
|
||||
required: true
|
||||
- label: I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
required: true
|
||||
- label: I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: help
|
||||
attributes:
|
||||
label: Commit to Help
|
||||
description: |
|
||||
After submitting this, I commit to one of:
|
||||
|
||||
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
||||
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
||||
* Implement a Pull Request for a confirmed bug.
|
||||
|
||||
options:
|
||||
- label: I commit to help with one of those options 👆
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: Example Code
|
||||
description: |
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
|
||||
|
||||
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
|
||||
|
||||
placeholder: |
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
render: python
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
What is the problem, question, or error?
|
||||
|
||||
Write a short description telling me what you are doing, what you expect to happen, and what is currently happening.
|
||||
placeholder: |
|
||||
* Open the browser and call the endpoint `/`.
|
||||
* It returns a JSON with `{"Hello": "World"}`.
|
||||
* But I expected it to return `{"Hello": "Sara"}`.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: What operating system are you on?
|
||||
multiple: true
|
||||
options:
|
||||
- Linux
|
||||
- Windows
|
||||
- macOS
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: os-details
|
||||
attributes:
|
||||
label: Operating System Details
|
||||
description: You can add more details about your operating system here, in particular if you chose "Other".
|
||||
- type: input
|
||||
id: fastapi-version
|
||||
attributes:
|
||||
label: FastAPI Version
|
||||
description: |
|
||||
What FastAPI version are you using?
|
||||
|
||||
You can find the FastAPI version with:
|
||||
|
||||
```bash
|
||||
python -c "import fastapi; print(fastapi.__version__)"
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: python-version
|
||||
attributes:
|
||||
label: Python Version
|
||||
description: |
|
||||
What Python version are you using?
|
||||
|
||||
You can find the Python version with:
|
||||
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any additional context information or screenshots you think are useful.
|
||||
7
.github/actions/notify-translations/Dockerfile
vendored
Normal 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"]
|
||||
10
.github/actions/notify-translations/action.yml
vendored
Normal 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'
|
||||
104
.github/actions/notify-translations/app/main.py
vendored
Normal 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")
|
||||
14
.github/actions/notify-translations/app/translations.yml
vendored
Normal 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
|
||||
24
.github/actions/people/app/main.py
vendored
@@ -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(
|
||||
|
||||
21
.github/workflows/notify-translations.yml
vendored
Normal 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 }}
|
||||
6
.github/workflows/test.yml
vendored
@@ -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
|
||||
|
||||
15
README.md
@@ -5,8 +5,8 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
|
||||
@@ -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
@@ -0,0 +1,203 @@
|
||||
# Merkmale
|
||||
|
||||
## FastAPI Merkmale
|
||||
|
||||
**FastAPI** ermöglicht Ihnen folgendes:
|
||||
|
||||
### Basiert auf offenen Standards
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> für API-Erstellung, zusammen mit Deklarationen von <abbr title="auch genannt: Endpunkte, Routen">Pfad</abbr> <abbr title="gemeint sind: HTTP-Methoden, wie POST, GET, PUT, DELETE">Operationen</abbr>, Parameter, Nachrichtenrumpf-Anfragen (englisch: body request), Sicherheit, etc.
|
||||
* Automatische Dokumentation der Datenentitäten mit dem <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (OpenAPI basiert selber auf dem JSON Schema).
|
||||
* Entworfen auf Grundlage dieser Standards nach einer sorgfältigen Studie, statt einer nachträglichen Schicht über diesen Standards.
|
||||
* Dies ermöglicht automatische **Quellcode-Generierung auf Benutzerebene** in vielen Sprachen.
|
||||
|
||||
### Automatische Dokumentation
|
||||
|
||||
Mit einer interaktiven API-Dokumentation und explorativen webbasierten Benutzerschnittstellen. Da FastAPI auf OpenAPI basiert, gibt es hierzu mehrere Optionen, wobei zwei standartmäßig vorhanden sind.
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, bietet interaktive Exploration: testen und rufen Sie ihre API direkt vom Webbrowser auf.
|
||||
|
||||

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

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

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

|
||||
|
||||
Sie bekommen Autovervollständigung an Stellen, an denen Sie dies vorher nicht für möglich gehalten hätten. Zum Beispiel der `price` Schlüssel aus einem JSON Datensatz (dieser könnte auch verschachtelt sein) aus einer Anfrage.
|
||||
|
||||
Hierdurch werden Sie nie wieder einen falschen Schlüsselnamen benutzen und sparen sich lästiges Suchen in der Dokumentation, um beispielsweise herauszufinden ob Sie `username` oder `user_name` als Schlüssel verwenden.
|
||||
|
||||
### Kompakt
|
||||
|
||||
FastAPI nutzt für alles sensible **Standard-Einstellungen**, welche optional überall konfiguriert werden können. Alle Parameter können ganz genau an Ihre Bedürfnisse angepasst werden, sodass sie genau die API definieren können, die sie brachen.
|
||||
|
||||
Aber standartmäßig, **"funktioniert einfach"** alles.
|
||||
|
||||
### Validierung
|
||||
|
||||
* Validierung für die meisten (oder alle?) Python **Datentypen**, hierzu gehören:
|
||||
* JSON Objekte (`dict`).
|
||||
* JSON Listen (`list`), die den Typ ihrer Elemente definieren.
|
||||
* Zeichenketten (`str`), mit definierter minimaler und maximaler Länge.
|
||||
* Zahlen (`int`, `float`) mit minimaler und maximaler Größe, usw.
|
||||
|
||||
* Validierung für ungewögnliche Typen, wie:
|
||||
* URL.
|
||||
* Email.
|
||||
* UUID.
|
||||
* ... und andere.
|
||||
|
||||
Die gesamte Validierung übernimmt das etablierte und robuste **Pydantic**.
|
||||
|
||||
### Sicherheit und Authentifizierung
|
||||
|
||||
Sicherheit und Authentifizierung integriert. Ohne einen Kompromiss aufgrund einer Datenbank oder den Datenentitäten.
|
||||
|
||||
Unterstützt alle von OpenAPI definierten Sicherheitsschemata, hierzu gehören:
|
||||
|
||||
* HTTP Basis Authentifizierung.
|
||||
* **OAuth2** (auch mit **JWT Zugriffstokens**). Schauen Sie sich hierzu dieses Tutorial an: [OAuth2 mit JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
|
||||
* API Schlüssel in:
|
||||
* Kopfzeile (HTTP Header).
|
||||
* Anfrageparametern.
|
||||
* Cookies, etc.
|
||||
|
||||
Zusätzlich gibt es alle Sicherheitsfunktionen von Starlette (auch **session cookies**).
|
||||
|
||||
Alles wurde als wiederverwendbare Werkzeuge und Komponenten geschaffen, die einfach in ihre Systeme, Datenablagen, relationale und nicht-relationale Datenbanken, ..., integriert werden können.
|
||||
|
||||
### Einbringen von Abhängigkeiten (meist: Dependency Injection)
|
||||
|
||||
FastAPI enthält ein extrem einfaches, aber extrem mächtiges <abbr title='oft verwendet im Zusammenhang von: Komponenten, Resourcen, Diensten, Dienstanbieter'><strong>Dependency Injection</strong></abbr> System.
|
||||
|
||||
* Selbst Abhängigkeiten können Abhängigkeiten haben, woraus eine Hierachie oder ein **"Graph" von Abhängigkeiten** entsteht.
|
||||
* **Automatische Umsetzung** durch FastAPI.
|
||||
* Alle abhängigen Komponenten könnten Daten von Anfragen, **Erweiterungen der Pfadoperations-**Einschränkungen und der automatisierten Dokumentation benötigen.
|
||||
* **Automatische Validierung** selbst für *Pfadoperationen*-Parameter, die in den Abhängigkeiten definiert wurden.
|
||||
* Unterstütz komplexe Benutzerauthentifizierungssysteme, mit **Datenbankverbindungen**, usw.
|
||||
* **Keine Kompromisse** bei Datenbanken, Eingabemasken, usw. Sondern einfache Integration von allen.
|
||||
|
||||
### Unbegrenzte Erweiterungen
|
||||
|
||||
Oder mit anderen Worten, sie werden nicht benötigt. Importieren und nutzen Sie Quellcode nach Bedarf.
|
||||
|
||||
Jede Integration wurde so entworfen, dass sie einfach zu nutzen ist (mit Abhängigkeiten), sodass Sie eine Erweiterung für Ihre Anwendung mit nur zwei Zeilen an Quellcode implementieren können. Hierbei nutzen Sie die selbe Struktur und Syntax, wie bei Pfadoperationen.
|
||||
|
||||
### Getestet
|
||||
|
||||
* 100% <abbr title="Die Anzahl an Code, die automatisch getestet wird">Testabdeckung</abbr>.
|
||||
* 100% <abbr title="Python Typ Annotationen, mit dennen Ihr Editor und andere exteren Werkezuge Sie besser unterstützen können">Typen annotiert</abbr>.
|
||||
* Verwendet in Produktionsanwendungen.
|
||||
|
||||
## Starlette's Merkmale
|
||||
|
||||
**FastAPI** ist vollkommen kompatibel (und basiert auf) <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Das bedeutet, auch ihr eigner Starlett Quellcode funktioniert.
|
||||
|
||||
`FastAPI` ist eigentlich eine Unterklasse von `Starlette`. Wenn sie also bereits Starlette kennen oder benutzen, können Sie das meiste Ihres Wissen direkt anwenden.
|
||||
|
||||
Mit **FastAPI** bekommen Sie viele von **Starlette**'s Funktionen (da FastAPI nur Starlette auf Steroiden ist):
|
||||
|
||||
* Stark beeindruckende Performanz. Es ist <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">eines der schnellsten Python frameworks, auf Augenhöhe mit **NodeJS** und **Go**</a>.
|
||||
* **WebSocket**-Unterstützung.
|
||||
* Hintergrundaufgaben im selben Prozess.
|
||||
* Ereignisse für das Starten und Herunterfahren.
|
||||
* Testclient basierend auf `requests`.
|
||||
* **CORS**, GZip, statische Dateien, Antwortfluss.
|
||||
* **Sitzungs und Cookie** Unterstützung.
|
||||
* 100% Testabdeckung.
|
||||
* 100% Typen annotiert.
|
||||
|
||||
## Pydantic's Merkmale
|
||||
|
||||
**FastAPI** ist vollkommen kompatibel (und basiert auf) <a href="https://pydantic-docs.helpmanual.io" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Das bedeutet, auch jeder zusätzliche Pydantic Quellcode funktioniert.
|
||||
|
||||
Verfügbar sind ebenso externe auf Pydantic basierende Bibliotheken, wie <abbr title="Object-Relational Mapper (Abbildung von Objekten auf relationale Strukturen)">ORM</abbr>s, <abbr title="Object-Document Mapper (Abbildung von Objekten auf nicht-relationale Strukturen)">ODM</abbr>s für Datenbanken.
|
||||
|
||||
Daher können Sie in vielen Fällen das Objekt einer Anfrage **direkt zur Datenbank** schicken, weil alles automatisch validiert wird.
|
||||
|
||||
Das selbe gilt auch für die andere Richtung: Sie können jedes Objekt aus der Datenbank **direkt zum Klienten** schicken.
|
||||
|
||||
Mit **FastAPI** bekommen Sie alle Funktionen von **Pydantic** (da FastAPI für die gesamte Datenverarbeitung Pydantic nutzt):
|
||||
|
||||
* **Kein Kopfzerbrechen**:
|
||||
* Sie müssen keine neue Schemadefinitionssprache lernen.
|
||||
* Wenn Sie mit Python's Typisierung arbeiten können, können Sie auch mit Pydantic arbeiten.
|
||||
* Gutes Zusammenspiel mit Ihrer/Ihrem **<abbr title="Integrierten Entwicklungsumgebung, ähnlich zu (Quellcode-)Editor">IDE</abbr>/<abbr title="Ein Programm, was Fehler im Quellcode sucht">linter</abbr>/Gehirn**:
|
||||
* Weil Datenstrukturen von Pydantic einfach nur Instanzen ihrer definierten Klassen sind, sollten Autovervollständigung, Linting, mypy und ihre Intuition einwandfrei funktionieren.
|
||||
* **Schnell**:
|
||||
* In <a href="https://pydantic-docs.helpmanual.io/#benchmarks-tag" class="external-link" target="_blank">Vergleichen</a> ist Pydantic schneller als jede andere getestete Bibliothek.
|
||||
* Validierung von **komplexen Strukturen**:
|
||||
* Benutzung von hierachischen Pydantic Schemata, Python `typing`’s `List` und `Dict`, etc.
|
||||
* Validierungen erlauben klare und einfache Datenschemadefinition, überprüft und dokumentiert als JSON Schema.
|
||||
* Sie können stark **verschachtelte JSON** Objekte haben und diese sind trotzdem validiert und annotiert.
|
||||
* **Erweiterbar**:
|
||||
* Pydantic erlaubt die Definition von eigenen Datentypen oder sie können die Validierung mit einer `validator` dekorierten Methode erweitern..
|
||||
* 100% Testabdeckung.
|
||||
@@ -425,7 +425,6 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
@@ -452,7 +451,6 @@ Used by Starlette:
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
|
||||
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
|
||||
|
||||
Used by FastAPI / Starlette:
|
||||
|
||||
@@ -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:
|
||||
@@ -76,7 +78,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
379
docs/en/data/github_sponsors.yml
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
8
docs/en/data/sponsors_badge.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
logins:
|
||||
- jina-ai
|
||||
- deta
|
||||
- investsuite
|
||||
- vimsoHQ
|
||||
- mikeckennedy
|
||||
- koaning
|
||||
- deepset-ai
|
||||
@@ -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.
|
||||
|
||||
@@ -33,7 +33,7 @@ You should do it after adding all your *path operations*.
|
||||
|
||||
## Exclude from OpenAPI
|
||||
|
||||
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`;
|
||||
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
|
||||
@@ -50,3 +50,121 @@ It won't show up in the documentation, but other tools (such as Sphinx) will be
|
||||
```Python hl_lines="19-29"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Additional Responses
|
||||
|
||||
You probably have seen how to declare the `response_model` and `status_code` for a *path operation*.
|
||||
|
||||
That defines the metadata about the main response of a *path operation*.
|
||||
|
||||
You can also declare additional responses with their models, status codes, etc.
|
||||
|
||||
There's a whole chapter here in the documentation about it, you can read it at [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
## OpenAPI Extra
|
||||
|
||||
When you declare a *path operation* in your application, **FastAPI** automatically generates the relevant metadata about that *path operation* to be included in the OpenAPI schema.
|
||||
|
||||
!!! note "Technical details"
|
||||
In the OpenAPI specification it is called the <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Operation Object</a>.
|
||||
|
||||
It has all the information about the *path operation* and is used to generate the automatic documentation.
|
||||
|
||||
It includes the `tags`, `parameters`, `requestBody`, `responses`, etc.
|
||||
|
||||
This *path operation*-specific OpenAPI schema is normally generated automatically by **FastAPI**, but you can also extend it.
|
||||
|
||||
!!! tip
|
||||
This is a low level extension point.
|
||||
|
||||
If you only need to declare additonal responses, a more convenient way to do it is with [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
You can extend the OpenAPI schema for a *path operation* using the parameter `openapi_extra`.
|
||||
|
||||
### OpenAPI Extensions
|
||||
|
||||
This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial005.py!}
|
||||
```
|
||||
|
||||
If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*.
|
||||
|
||||
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
|
||||
|
||||
And if you see the resulting OpenAPI (at `/openapi.json` in your API), you will see your extension as part of the specific *path operation* too:
|
||||
|
||||
```JSON hl_lines="22"
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-aperture-labs-portal": "blue"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom OpenAPI *path operation* schema
|
||||
|
||||
The dictionary in `openapi_extra` will be deeply merged with the automatically generated OpenAPI schema for the *path operation*.
|
||||
|
||||
So, you could add additional data to the automatically generated schema.
|
||||
|
||||
For example, you could decide to read and validate the request with your own code, without using the automatic features of FastAPI with Pydantic, but you could still want to define the request in the OpenAPI schema.
|
||||
|
||||
You could do that with `openapi_extra`:
|
||||
|
||||
```Python hl_lines="20-37 39-40"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial006.py!}
|
||||
```
|
||||
|
||||
In this example, we didn't declare any Pydantic model. In fact, the request body is not even <abbr title="converted from some plain format, like bytes, into Python objects">parsed</abbr> as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
|
||||
|
||||
Nevertheless, we can declare the expected schema for the request body.
|
||||
|
||||
### Custom OpenAPI content type
|
||||
|
||||
Using this same trick, you could use a Pydantic model to define the JSON Schema that is then included in the custom OpenAPI schema section for the *path operation*.
|
||||
|
||||
And you could do this even if the data type in the request is not JSON.
|
||||
|
||||
For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON:
|
||||
|
||||
```Python hl_lines="17-22 24"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
|
||||
```
|
||||
|
||||
Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML.
|
||||
|
||||
Then we use the request directly, and extract the body as `bytes`. This means that FastAPI won't even try to parse the request payload as JSON.
|
||||
|
||||
And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content:
|
||||
|
||||
```Python hl_lines="26-33"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Here we re-use the same Pydantic model.
|
||||
|
||||
But the same way, we could have validated it in some other way.
|
||||
|
||||
@@ -235,7 +235,7 @@ And then we can require it from the *path operation function* as a dependency an
|
||||
|
||||
Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:
|
||||
|
||||
```Python hl_lines="8-9 12 21"
|
||||
```Python hl_lines="9-10 13 21"
|
||||
{!../../../docs_src/settings/app02/test_main.py!}
|
||||
```
|
||||
|
||||
@@ -288,7 +288,7 @@ Reading a file from disk is normally a costly (slow) operation, so you probably
|
||||
But every time we do:
|
||||
|
||||
```Python
|
||||
config.Settings()
|
||||
Settings()
|
||||
```
|
||||
|
||||
a new `Settings` object would be created, and at creation it would read the `.env` file again.
|
||||
@@ -297,7 +297,7 @@ If the dependency function was just like:
|
||||
|
||||
```Python
|
||||
def get_settings():
|
||||
return config.Settings()
|
||||
return Settings()
|
||||
```
|
||||
|
||||
we would create that object for each request, and we would be reading the `.env` file for each request. ⚠️
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -102,13 +102,15 @@ To see the difference, imagine the following story about burgers:
|
||||
|
||||
### Concurrent Burgers
|
||||
|
||||
<!-- The gender neutral cook emoji "🧑🍳" does not render well in browsers. In the meantime, I'm using a mix of male "👨🍳" and female "👩🍳" cooks. -->
|
||||
|
||||
You go with your crush 😍 to get fast food 🍔, you stand in line while the cashier 💁 takes the orders from the people in front of you.
|
||||
|
||||
Then it's your turn, you place your order of 2 very fancy burgers 🍔 for your crush 😍 and you.
|
||||
|
||||
You pay 💸.
|
||||
|
||||
The cashier 💁 says something to the guy in the kitchen 👨🍳 so he knows he has to prepare your burgers 🍔 (even though he is currently preparing the ones for the previous clients).
|
||||
The cashier 💁 says something to the cook in the kitchen 👨🍳 so they know they have to prepare your burgers 🍔 (even though they are currently preparing the ones for the previous clients).
|
||||
|
||||
The cashier 💁 gives you the number of your turn.
|
||||
|
||||
@@ -146,9 +148,9 @@ Now let's imagine these aren't "Concurrent Burgers", but "Parallel Burgers".
|
||||
|
||||
You go with your crush 😍 to get parallel fast food 🍔.
|
||||
|
||||
You stand in line while several (let's say 8) cashiers that at the same time are cooks 👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳 take the orders from the people in front of you.
|
||||
You stand in line while several (let's say 8) cashiers that at the same time are cooks 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳 take the orders from the people in front of you.
|
||||
|
||||
Everyone before you is waiting 🕙 for their burgers 🍔 to be ready before leaving the counter because each of the 8 cashiers goes himself and prepares the burger right away before getting the next order.
|
||||
Everyone before you is waiting 🕙 for their burgers 🍔 to be ready before leaving the counter because each of the 8 cashiers goes and prepares the burger right away before getting the next order.
|
||||
|
||||
Then it's finally your turn, you place your order of 2 very fancy burgers 🍔 for your crush 😍 and you.
|
||||
|
||||
@@ -174,7 +176,7 @@ There was not much talk or flirting as most of the time was spent waiting 🕙 i
|
||||
|
||||
In this scenario of the parallel burgers, you are a computer / program 🤖 with two processors (you and your crush 😍), both waiting 🕙 and dedicating their attention ⏯ to be "waiting on the counter" 🕙 for a long time.
|
||||
|
||||
The fast food store has 8 processors (cashiers/cooks) 👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳. While the concurrent burgers store might have had only 2 (one cashier and one cook) 💁 👨🍳.
|
||||
The fast food store has 8 processors (cashiers/cooks) 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳. While the concurrent burgers store might have had only 2 (one cashier and one cook) 💁 👨🍳.
|
||||
|
||||
But still, the final experience is not the best 😞.
|
||||
|
||||
@@ -236,7 +238,7 @@ You could have turns as in the burgers example, first the living room, then the
|
||||
|
||||
It would take the same amount of time to finish with or without turns (concurrency) and you would have done the same amount of work.
|
||||
|
||||
But in this case, if you could bring the 8 ex-cashier/cooks/now-cleaners 👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳, and each one of them (plus you) could take a zone of the house to clean it, you could do all the work in **parallel**, with the extra help, and finish much sooner.
|
||||
But in this case, if you could bring the 8 ex-cashier/cooks/now-cleaners 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳, and each one of them (plus you) could take a zone of the house to clean it, you could do all the work in **parallel**, with the extra help, and finish much sooner.
|
||||
|
||||
In this scenario, each one of the cleaners (including you) would be a processor, doing their part of the job.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
@@ -99,3 +114,7 @@ a.announce-link:hover {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.twitter {
|
||||
color: #00acee;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
max-width: 100%;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 18px;
|
||||
/* font-size: 18px; */
|
||||
font-size: 15px;
|
||||
/* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */
|
||||
font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;
|
||||
border-radius: 4px;
|
||||
|
||||
311
docs/en/docs/deployment/concepts.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Deployments Concepts
|
||||
|
||||
When deploying a **FastAPI** application, or actually, any type of web API, there are several concepts that you probably care about, and using them you can find the **most appropriate** way to **deploy your application**.
|
||||
|
||||
Some of the important concepts are:
|
||||
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* Replication (the number of processes running)
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
We'll see how they would affect **deployments**.
|
||||
|
||||
In the end, the ultimate objective is to be able to **serve your API clients** in a way that is **secure**, to **avoid disruptions**, and to use the **compute resources** (for example remote servers/virtual machines) as efficiently as possible. 🚀
|
||||
|
||||
I'll tell you a bit more about these **concepts** here, and that would hopefully give you the **intuition** you would need to decide how to deploy your API in very different environments, possibly even in **future** ones that don't exist yet.
|
||||
|
||||
By considering these concepts, you will be able to **evaluate and design** the best way to deploy **your own APIs**.
|
||||
|
||||
In the next chapters, I'll give you more **concrete recipes** to deploy FastAPI applications.
|
||||
|
||||
But for now, let's check these important **conceptual ideas**. These concepts also apply 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. 🚀
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
## 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. 🤓
|
||||
|
||||
@@ -7,42 +7,184 @@ But it is way more complex than that.
|
||||
!!! tip
|
||||
If you are in a hurry or don't care, continue with the next sections for step by step instructions to set everything up with different techniques.
|
||||
|
||||
To learn the basics of HTTPS, from a consumer perspective, check <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
To **learn the basics of HTTPS**, from a consumer perspective, check <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
|
||||
Now, from a developer's perspective, here are several things to have in mind while thinking about HTTPS:
|
||||
Now, from a **developer's perspective**, here are several things to have in mind while thinking about HTTPS:
|
||||
|
||||
* For HTTPS, the server needs to have "certificates" generated by a third party.
|
||||
* Those certificates are actually acquired from the third-party, not "generated".
|
||||
* Certificates have a lifetime.
|
||||
* They expire.
|
||||
* And then they need to be renewed, acquired again from the third party.
|
||||
* The encryption of the connection happens at the TCP level.
|
||||
* That's one layer below HTTP.
|
||||
* So, the certificate and encryption handling is done before HTTP.
|
||||
* TCP doesn't know about "domains". Only about IP addresses.
|
||||
* The information about the specific domain requested goes in the HTTP data.
|
||||
* The HTTPS certificates "certify" a certain domain, but the protocol and encryption happen at the TCP level, before knowing which domain is being dealt with.
|
||||
* By default, that would mean that you can only have one HTTPS certificate per IP address.
|
||||
* For HTTPS, **the server** needs to **have "certificates"** generated by a **third party**.
|
||||
* Those certificates are actually **acquired** from the third party, not "generated".
|
||||
* Certificates have a **lifetime**.
|
||||
* They **expire**.
|
||||
* And then they need to be **renewed**, **acquired again** from the third party.
|
||||
* The encryption of the connection happens at the **TCP level**.
|
||||
* That's one layer **below HTTP**.
|
||||
* So, the **certificate and encryption** handling is done **before HTTP**.
|
||||
* **TCP doesn't know about "domains"**. Only about IP addresses.
|
||||
* The information about the **specific domain** requested goes in the **HTTP data**.
|
||||
* The **HTTPS certificates** "certify" a **certain domain**, but the protocol and encryption happen at the TCP level, **before knowing** which domain is being dealt with.
|
||||
* **By default**, that would mean that you can only have **one HTTPS certificate per IP address**.
|
||||
* No matter how big your server is or how small each application you have on it might be.
|
||||
* There is a solution to this, however.
|
||||
* There's an extension to the TLS protocol (the one handling the encryption at the TCP level, before HTTP) called <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>.
|
||||
* This SNI extension allows one single server (with a single IP address) to have several HTTPS certificates and serve multiple HTTPS domains/applications.
|
||||
* For this to work, a single component (program) running on the server, listening on the public IP address, must have all the HTTPS certificates in the server.
|
||||
* After obtaining a secure connection, the communication protocol is still HTTP.
|
||||
* The contents are encrypted, even though they are being sent with the HTTP protocol.
|
||||
* There is a **solution** to this, however.
|
||||
* There's an **extension** to the **TLS** protocol (the one handling the encryption at the TCP level, before HTTP) called **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>**.
|
||||
* This SNI extension allows one single server (with a **single IP address**) to have **several HTTPS certificates** and serve **multiple HTTPS domains/applications**.
|
||||
* For this to work, a **single** component (program) running on the server, listening on the **public IP address**, must have **all the HTTPS certificates** in the server.
|
||||
* **After** obtaining a secure connection, the communication protocol is **still HTTP**.
|
||||
* The contents are **encrypted**, even though they are being sent with the **HTTP protocol**.
|
||||
|
||||
It is a common practice to have one program/HTTP server running on the server (the machine, host, etc.) and managing all the HTTPS parts : sending the decrypted HTTP requests to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the HTTP response from the application, encrypt it using the appropriate certificate and sending it back to the client using HTTPS. This server is often called a <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>.
|
||||
It is a common practice to have **one program/HTTP server** running on the server (the machine, host, etc.) and **managing all the HTTPS parts**: receiving the **encrypted HTTPS requests**, sending the **decrypted HTTP requests** to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the **HTTP response** from the application, **encrypt it** using the appropriate **HTTPS certificate** and sending it back to the client using **HTTPS**. This server is often called a **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>**.
|
||||
|
||||
Some of the options you could use as a TLS Termination Proxy are:
|
||||
|
||||
* Traefik (that can also handle certificate renewals)
|
||||
* Caddy (that can also handle certificate renewals)
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
## Let's Encrypt
|
||||
|
||||
Before Let's Encrypt, these HTTPS certificates were sold by trusted third-parties.
|
||||
Before Let's Encrypt, these **HTTPS certificates** were sold by trusted third parties.
|
||||
|
||||
The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive.
|
||||
|
||||
But then <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> was created.
|
||||
But then **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** was created.
|
||||
|
||||
It is a project from the Linux Foundation. It provides HTTPS certificates for free. In an automated way. These certificates use all the standard cryptographic security, and are short lived (about 3 months), so the security is actually better because of their reduced lifespan.
|
||||
It is a project from the Linux Foundation. It provides **HTTPS certificates for free**, in an automated way. These certificates use all the standard cryptographic security, and are short-lived (about 3 months), so the **security is actually better** because of their reduced lifespan.
|
||||
|
||||
The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates.
|
||||
|
||||
The idea is to automate the acquisition and renewal of these certificates, so that you can have secure HTTPS, for free, forever.
|
||||
The idea is to automate the acquisition and renewal of these certificates so that you can have **secure HTTPS, for free, forever**.
|
||||
|
||||
## HTTPS for Developers
|
||||
|
||||
Here's an example of how an HTTPS API could look like, step by step, paying attention mainly to the ideas important for developers.
|
||||
|
||||
### Domain Name
|
||||
|
||||
It would probably all start by you **acquiring** some **domain name**. Then, you would configure it in a DNS server (possibly your same cloud provider).
|
||||
|
||||
You would probably get a cloud server (a virtual machine) or something similar, and it would have a <abbr title="That doesn't change">fixed</abbr> **public IP address**.
|
||||
|
||||
In the DNS server(s) you would configure a record (an "`A record`") to point **your domain** to the public **IP address of your server**.
|
||||
|
||||
You would probably do this just once, the first time, when setting everything up.
|
||||
|
||||
!!! tip
|
||||
This Domain Name part is way before HTTPS, but as everything depends on the domain and the IP address, it's worth mentioning it here.
|
||||
|
||||
### DNS
|
||||
|
||||
Now let's focus on all the actual HTTPS parts.
|
||||
|
||||
First, the browser would check with the **DNS servers** what is the **IP for the domain**, in this case, `someapp.example.com`.
|
||||
|
||||
The DNS servers would tell the browser to use some specific **IP address**. That would be the public IP address used by your server, that you configured in the DNS servers.
|
||||
|
||||
<img src="/img/deployment/https/https01.svg">
|
||||
|
||||
### TLS Handshake Start
|
||||
|
||||
The browser would then communicate with that IP address on **port 443** (the HTTPS port).
|
||||
|
||||
The first part of the communication is just to establish the connection between the client and the server and to decide the cryptographic keys they will use, etc.
|
||||
|
||||
<img src="/img/deployment/https/https02.svg">
|
||||
|
||||
This interaction between the client and the server to establish the TLS connection is called the **TLS handshake**.
|
||||
|
||||
### TLS with SNI Extension
|
||||
|
||||
**Only one process** in the server can be listening on a specific **port** in a specific **IP address**. There could be other processes listening on other ports in the same IP address, but only one for each combination of IP address and port.
|
||||
|
||||
TLS (HTTPS) uses the specific port `443` by default. So that's the port we would need.
|
||||
|
||||
As only one process can be listening on this port, the process that would do it would be the **TLS Termination Proxy**.
|
||||
|
||||
The TLS Termination Proxy would have access to one or more **TLS certificates** (HTTPS certificates).
|
||||
|
||||
Using the **SNI extension** discussed above, the TLS Termination Proxy would check which of the TLS (HTTPS) certificates available it should use for this connection, using the one that matches the domain expected by the client.
|
||||
|
||||
In this case, it would use the certificate for `someapp.example.com`.
|
||||
|
||||
<img src="/img/deployment/https/https03.svg">
|
||||
|
||||
The client already **trusts** the entity that generated that TLS certificate (in this case Let's Encrypt, but we'll see about that later), so it can **verify** that the certificate is valid.
|
||||
|
||||
Then, using the certificate, the client and the TLS Termination Proxy **decide how to encrypt** the rest of the **TCP communication**. This completes the **TLS Handshake** part.
|
||||
|
||||
After this, the client and the server have an **encrypted TCP connection**, this is what TLS provides. And then they can use that connection to start the actual **HTTP communication**.
|
||||
|
||||
And that's what **HTTPS** is, it's just plain **HTTP** inside a **secure TLS connection** instead of a pure (unencrypted) TCP connection.
|
||||
|
||||
!!! tip
|
||||
Notice that the encryption of the communication happens at the **TCP level**, not at the HTTP level.
|
||||
|
||||
### HTTPS Request
|
||||
|
||||
Now that the client and server (specifically the browser and the TLS Termination Proxy) have an **encrypted TCP connection**, they can start the **HTTP communication**.
|
||||
|
||||
So, the client sends an **HTTPS request**. This is just an HTTP request through an encrypted TLS connection.
|
||||
|
||||
<img src="/img/deployment/https/https04.svg">
|
||||
|
||||
### Decrypt the Request
|
||||
|
||||
The TLS Termination Proxy would use the encryption agreed to **decrypt the request**, and would transmit the **plain (decrypted) HTTP request** to the process running the application (for example a process with Uvicorn running the FastAPI application).
|
||||
|
||||
<img src="/img/deployment/https/https05.svg">
|
||||
|
||||
### HTTP Response
|
||||
|
||||
The application would process the request and send a **plain (unencrypted) HTTP response** to the TLS Termination Proxy.
|
||||
|
||||
<img src="/img/deployment/https/https06.svg">
|
||||
|
||||
### HTTPS Response
|
||||
|
||||
The TLS Termination Proxy would then **encrypt the response** using the cryptography agreed before (that started with the certificate for `someapp.example.com`), and send it back to the browser.
|
||||
|
||||
Next, the browser would verify that the response is valid and encrypted with the right cryptographic key, etc. It would then **decrypt the response** and process it.
|
||||
|
||||
<img src="/img/deployment/https/https07.svg">
|
||||
|
||||
The client (browser) will know that the response comes from the correct server because it is using the cryptography they agreed using the **HTTPS certificate** before.
|
||||
|
||||
### Multiple Applications
|
||||
|
||||
In the same server (or servers), there could be **multiple applications**, for example, other API programs or a database.
|
||||
|
||||
Only one process can be handling the specific IP and port (the TLS Termination Proxy in our example) but the other applications/processes can be running on the server(s) too, as long as they don't try to use the same **combination of public IP and port**.
|
||||
|
||||
<img src="/img/deployment/https/https08.svg">
|
||||
|
||||
That way, the TLS Termination Proxy could handle HTTPS and certificates for **multiple domains**, for multiple applications, and then transmit the requests to the right application in each case.
|
||||
|
||||
### Certificate Renewal
|
||||
|
||||
At some point in the future, each certificate would **expire** (about 3 months after acquiring it).
|
||||
|
||||
And then, there would be another program (in some cases it's another program, in some cases it could be the same TLS Termination Proxy) that would talk to Let's Encrypt, and renew the certificate(s).
|
||||
|
||||
<img src="/img/deployment/https/https.svg">
|
||||
|
||||
The **TLS certificates** are **associated with a domain name**, not with an IP address.
|
||||
|
||||
So, to renew the certificates, the renewal program needs to **prove** to the authority (Let's Encrypt) that it indeed **"owns" and controls that domain**.
|
||||
|
||||
To do that, and to accommodate different application needs, there are several ways it can do it. Some popular ways are:
|
||||
|
||||
* **Modify some DNS records**.
|
||||
* For this, the renewal program needs to support the APIs of the DNS provider, so, depending on the DNS provider you are using, this might or might not be an option.
|
||||
* **Run as a server** (at least during the certificate acquisition process) on the public IP address associated with the domain.
|
||||
* As we said above, only one process can be listening on a specific IP and port.
|
||||
* This is one of the reasons why it's very useful when the same TLS Termination Proxy also takes care of the certificate renewal process.
|
||||
* Otherwise, you might have to stop the TLS Termination Proxy momentarily, start the renewal program to acquire the certificates, then configure them with the TLS Termination Proxy, and then restart the TLS Termination Proxy. This is not ideal, as your app(s) will not be available during the time that the TLS Termination Proxy is off.
|
||||
|
||||
All this renewal process, while still serving the app, is one of the main reasons why you would want to have a **separate system to handle HTTPS** with a TLS Termination Proxy instead of just using the TLS certificates with the application server directly (e.g. Uvicorn).
|
||||
|
||||
## Recap
|
||||
|
||||
Having **HTTPS** is very important, and quite **critical** in most cases. Most of the effort you as a developer have to put around HTTPS is just about **understanding these concepts** and how they work.
|
||||
|
||||
But once you know the basic information of **HTTPS for developers** you can easily combine and configure different tools to help you manage everything in a simple way.
|
||||
|
||||
In some of the next chapters I'll show you several concrete examples of how to set up **HTTPS** for **FastAPI** applications. 🔒
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
|
||||
Deploying a **FastAPI** application is relatively easy.
|
||||
|
||||
## What Does Deployment Mean
|
||||
|
||||
To **deploy** an application means to perform the necessary steps to make it **available to the users**.
|
||||
|
||||
For a **web API**, it normally involves putting it in a **remote machine**, with a **server program** that provides good performance, stability, etc, so that your **users** can **access** the application efficiently and without interruptions or problems.
|
||||
|
||||
This is in contrast to the the **development** stages, where you are constantly changing the code, breaking it and fixing it, stopping and restarting the development server, etc.
|
||||
|
||||
## Deployment Strategies
|
||||
|
||||
There are several ways to do it depending on your specific use case and the tools that you use.
|
||||
|
||||
You will see more details to have in mind and some of the techniques to do it in the next sections.
|
||||
You could **deploy a server** yourself using a combination of tools, you could use a **cloud service** that does part of the work for you, or other possible options.
|
||||
|
||||
I will show you some of the main concepts you should probably have in mind when deploying a **FastAPI** application (although most of it applies to any other type of web application).
|
||||
|
||||
You will see more details to have in mind and some of the techniques to do it in the next sections. ✨
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
# Deploy manually
|
||||
# Run a Server Manually - Uvicorn
|
||||
|
||||
You can deploy **FastAPI** manually as well.
|
||||
The main thing you need to run a **FastAPI** application in a remote server machine is an ASGI server program like **Uvicorn**.
|
||||
|
||||
You just need to install an ASGI compatible server like:
|
||||
There are 3 main alternatives:
|
||||
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: a high performance ASGI server.
|
||||
* <a href="https://pgjones.gitlab.io/hypercorn/" class="external-link" target="_blank">Hypercorn</a>: an ASGI server compatible with HTTP/2 and Trio among other features.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: the ASGI server built for Django Channels.
|
||||
|
||||
## Server Machine and Server Program
|
||||
|
||||
There's a small detail about names to have in mind. 💡
|
||||
|
||||
The word "**server**" is commonly used to refer to both the remote/cloud computer (the physical or virtual machine) and also the program that is running on that machine (e.g. Uvicorn).
|
||||
|
||||
Just have that in mind when you read "server" in general, it could refer to one of those two things.
|
||||
|
||||
When referring to the remote machine, it's common to call it **server**, but also **machine**, **VM** (virtual machine), **node**. Those all refer to some type of remote machine, normally running Linux, where you run programs.
|
||||
|
||||
## Install the Server Program
|
||||
|
||||
You can install an ASGI compatible server with:
|
||||
|
||||
=== "Uvicorn"
|
||||
|
||||
@@ -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. 🚀
|
||||
|
||||
178
docs/en/docs/deployment/server-workers.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# Server Workers - Gunicorn with Uvicorn
|
||||
|
||||
Let's check back those deployment concepts from before:
|
||||
|
||||
* Security - HTTPS
|
||||
* Running on startup
|
||||
* Restarts
|
||||
* **Replication (the number of processes running)**
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
Up to this point, with all the tutorials in the docs, you have probably been running a **server program** like Uvicorn, running a **single process**.
|
||||
|
||||
When deploying applications you will probably want to have some **replication of processes** to take advantage of **multiple cores** and to be able to handle more requests.
|
||||
|
||||
As you saw in the previous chapter about [Deployment Concepts](./concepts.md){.internal-link target=_blank}, there are multiple strategies you can use.
|
||||
|
||||
Here I'll show you how to use <a href="https://gunicorn.org/" class="external-link" target="_blank">**Gunicorn**</a> with **Uvicorn worker processes**.
|
||||
|
||||
!!! info
|
||||
If you are using containers, for example with Docker or Kubernetes, I'll tell you more about that in the next chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}.
|
||||
|
||||
In particular, when running on **Kubernetes** you will probably **not** want to use Gunicorn, and instead run **a single Uvicorn process per container**, but I'll tell you about it later in that chapter.
|
||||
|
||||
## Gunicorn with Uvicorn Workers
|
||||
|
||||
**Gunicorn** is mainly an application server using the **WSGI standard**. That means that Gunicorn can serve applications like Flask and Django. Gunicorn by itself is not compatible with **FastAPI**, as FastAPI uses the newest **<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI standard</a>**.
|
||||
|
||||
But Gunicorn supports working as a **process manager** and allowing users to tell it which specific **worker process class** to use. Then Gunicorn would start one or more **worker processes** using that class.
|
||||
|
||||
And **Uvicorn** has a **Gunicorn-compatible worker class**.
|
||||
|
||||
Using that combination, Gunicorn would act as a **process manager**, listening on the **port** and the **IP**. And it would **transmit** the communication to the worker processes running the **Uvicorn class**.
|
||||
|
||||
And then the Gunicorn-compatible **Uvicorn worker** class would be in charge of converting the data sent by Gunicorn to the ASGI standard for FastAPI to use it.
|
||||
|
||||
## Install Gunicorn and Uvicorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install uvicorn[standard] gunicorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
That will install both Uvicorn with the `standard` extra packages (to get high performance) and Gunicorn.
|
||||
|
||||
## Run Gunicorn with Uvicorn Workers
|
||||
|
||||
Then you can run Gunicorn with:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
|
||||
|
||||
[19499] [INFO] Starting gunicorn 20.1.0
|
||||
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
|
||||
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
|
||||
[19511] [INFO] Booting worker with pid: 19511
|
||||
[19513] [INFO] Booting worker with pid: 19513
|
||||
[19514] [INFO] Booting worker with pid: 19514
|
||||
[19515] [INFO] Booting worker with pid: 19515
|
||||
[19511] [INFO] Started server process [19511]
|
||||
[19511] [INFO] Waiting for application startup.
|
||||
[19511] [INFO] Application startup complete.
|
||||
[19513] [INFO] Started server process [19513]
|
||||
[19513] [INFO] Waiting for application startup.
|
||||
[19513] [INFO] Application startup complete.
|
||||
[19514] [INFO] Started server process [19514]
|
||||
[19514] [INFO] Waiting for application startup.
|
||||
[19514] [INFO] Application startup complete.
|
||||
[19515] [INFO] Started server process [19515]
|
||||
[19515] [INFO] Waiting for application startup.
|
||||
[19515] [INFO] Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Let's see what each of those options mean:
|
||||
|
||||
* `main:app`: This is the same syntax used by Uvicorn, `main` means the Python module named "`main`", so, a file `main.py`. And `app` is the name of the variable that is the **FastAPI** application.
|
||||
* You can imagine that `main:app` is equivalent to a Python `import` statement like:
|
||||
|
||||
```Python
|
||||
from main import app
|
||||
```
|
||||
|
||||
* So, the colon in `main:app` would be equivalent to the Python `import` part in `from main import app`.
|
||||
* `--workers`: The number of worker processes to use, each will run a Uvicorn worker, in this case 4 workers.
|
||||
* `--worker-class`: The Gunicorn-compatible worker class to use in the worker processes.
|
||||
* Here we pass the class that Gunicorn can import and use with:
|
||||
|
||||
```Python
|
||||
import uvicorn.workers.UvicornWorker
|
||||
```
|
||||
|
||||
* `--bind`: This tells Gunicorn the IP and the port to listen to, using a colon (`:`) to separate the IP and the port.
|
||||
* If you were running Uvicorn directly, instead of `--bind 0.0.0.0:80` (the Gunicorn option) you would use `--host 0.0.0.0` and `--port 80`.
|
||||
|
||||
In the output you can see that it shows the **PID** (process ID) of each process (it's just a number).
|
||||
|
||||
You can see that:
|
||||
|
||||
* The Gunicorn **process manager** starts with PID `19499` (in your case it will be a different number).
|
||||
* Then it starts `Listening at: http://0.0.0.0:80`.
|
||||
* Then it detects that it has to use the worker class at `uvicorn.workers.UvicornWorker`.
|
||||
* And then it starts **4 workers**, each with its own PID: `19511`, `19513`, `19514`, and `19515`.
|
||||
|
||||
Gunicorn would also take care of managing **dead processes** and **restarting** new ones if needed to keep the number of workers. So that helps in part with the **restart** concept from the list above.
|
||||
|
||||
Nevertheless, you would probably also want to have something outside making sure to **restart Gunicorn** if necessary, and also to **run it on startup**, etc.
|
||||
|
||||
## Uvicorn with Workers
|
||||
|
||||
Uvicorn also has an option to start and run several **worker processes**.
|
||||
|
||||
Nevertheless, as of now, Uvicorn's capabilities for handling worker processes are more limited than Gunicorn's. So, if you want to have a process manager at this level (at the Python level), then it might be better to try with Gunicorn as the process manager.
|
||||
|
||||
In any case, you would run it like this:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
|
||||
<font color="#A6E22E">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
|
||||
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The only new option here is `--workers` telling Uvicorn to start 4 worker processes.
|
||||
|
||||
You can also see that it shows the **PID** of each process, `27365` for the parent process (this is the **process manager**) and one for each worker process: `27368`, `27369`, `27370`, and `27367`.
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
Here you saw how to use **Gunicorn** (or Uvicorn) managing **Uvicorn worker processes** to **parallelize** the execution of the application, take advantage of **multiple cores** in the CPU, and be able to serve **more requests**.
|
||||
|
||||
From the list of deployment concepts from above, using workers would mainly help with the **replication** part, and a little bit with the **restarts**, but you still need to take care of the others:
|
||||
|
||||
* **Security - HTTPS**
|
||||
* **Running on startup**
|
||||
* ***Restarts***
|
||||
* Replication (the number of processes running)
|
||||
* **Memory**
|
||||
* **Previous steps before starting**
|
||||
|
||||
## Containers and Docker
|
||||
|
||||
In the next chapter about [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank} I'll tell some strategies you could use to handle the other **deployment concepts**.
|
||||
|
||||
I'll also show you the **official Docker image** that includes **Gunicorn with Uvicorn workers** and some default configurations that can be useful for simple cases.
|
||||
|
||||
There I'll also show you how to **build your own image from scratch** to run a single Uvicorn process (without Gunicorn). It is a simple process and is probably what you would want to do when using a distributed container management system like **Kubernetes**.
|
||||
|
||||
## Recap
|
||||
|
||||
You can use **Gunicorn** (or also Uvicorn) as a process manager with Uvicorn workers to take advantage of **multi-core CPUs**, to run **multiple processes in parallel**.
|
||||
|
||||
You could use these tools and ideas if you are setting up **your own deployment system** while taking care of the other deployment concepts yourself.
|
||||
|
||||
Check out the next chapter to learn about **FastAPI** with containers (e.g. Docker and Kubernetes). You will see that those tools have simple ways to solve the other **deployment concepts** as well. ✨
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -20,6 +20,10 @@ You can subscribe to the (infrequent) [**FastAPI and friends** newsletter](/news
|
||||
* Breaking changes 🚨
|
||||
* Tips and tricks ✅
|
||||
|
||||
## Follow FastAPI on Twitter
|
||||
|
||||
<a href="https://twitter.com/fastapi" class="external-link" target="_blank">Follow @fastapi on **Twitter**</a> to get the latest news about **FastAPI**. 🐦
|
||||
|
||||
## Star **FastAPI** in GitHub
|
||||
|
||||
You can "star" FastAPI in GitHub (clicking the star button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. ⭐️
|
||||
@@ -32,7 +36,7 @@ You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right)
|
||||
|
||||
There you can select "Releases only".
|
||||
|
||||
Doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **FastAPI** with bug fixes and new features.
|
||||
By doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **FastAPI** with bug fixes and new features.
|
||||
|
||||
## Connect with the author
|
||||
|
||||
@@ -46,17 +50,18 @@ You can:
|
||||
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a>.
|
||||
* Tell me how you use FastAPI (I love to hear that).
|
||||
* Hear when I make announcements or release new tools.
|
||||
* You can also <a href="https://twitter.com/fastapi" class="external-link" target="_blank">follow @fastapi on Twitter</a> (a separate account).
|
||||
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Connect with me on **Linkedin**</a>.
|
||||
* Hear when I make announcements or release new tools (although I use Twitter more often 🤷♂).
|
||||
* Read what I write (or follow me) on <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> or <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
|
||||
* Read other ideas, articles, and about tools I have created.
|
||||
* Read other ideas, articles, and read about tools I have created.
|
||||
* Follow me to read when I publish something new.
|
||||
|
||||
## Tweet about **FastAPI**
|
||||
|
||||
<a href="https://twitter.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/tiangolo/fastapi cc @tiangolo" class="external-link" target="_blank">Tweet about **FastAPI**</a> and let me and others know why you like it. 🎉
|
||||
<a href="https://twitter.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/tiangolo/fastapi" class="external-link" target="_blank">Tweet about **FastAPI**</a> and let me and others know why you like it. 🎉
|
||||
|
||||
I love to hear about how **FastAPI** is being used, what have you liked in it, in which project/company are you using it, etc.
|
||||
I love to hear about how **FastAPI** is being used, what you have liked in it, in which project/company are you using it, etc.
|
||||
|
||||
## Vote for FastAPI
|
||||
|
||||
@@ -67,15 +72,15 @@ I love to hear about how **FastAPI** is being used, what have you liked in it, i
|
||||
|
||||
You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. 🤓
|
||||
|
||||
If you are helping a lot of people on issues you might become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉
|
||||
If you are helping a lot of people with issues, you might become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉
|
||||
|
||||
## Watch the GitHub repository
|
||||
|
||||
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. 👀
|
||||
|
||||
If you select "Watching" instead of "Releases only", you will receive notifications when someone creates a new issue.
|
||||
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue.
|
||||
|
||||
Then you can try and help them solving those issues.
|
||||
Then you can try and help them solve those issues.
|
||||
|
||||
## Create issues
|
||||
|
||||
@@ -84,7 +89,7 @@ You can <a href="https://github.com/tiangolo/fastapi/issues/new/choose" class="e
|
||||
* Ask a **question** or ask about a **problem**.
|
||||
* Suggest a new **feature**.
|
||||
|
||||
**Note**: if you create an issue then I'm going to ask you to also help others. 😉
|
||||
**Note**: if you create an issue, then I'm going to ask you to also help others. 😉
|
||||
|
||||
## Create a Pull Request
|
||||
|
||||
@@ -94,7 +99,7 @@ You can [contribute](contributing.md){.internal-link target=_blank} to the sourc
|
||||
* To share an article, video, or podcast you created or found about FastAPI by <a href="https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">editing this file</a>.
|
||||
* Make sure you add your link to the start of the corresponding section.
|
||||
* To help [translate the documentation](contributing.md#translations){.internal-link target=_blank} to your language.
|
||||
* You can also help reviewing the translations created by others.
|
||||
* You can also help to review the translations created by others.
|
||||
* To propose new documentation sections.
|
||||
* To fix an existing issue/bug.
|
||||
* To add a new feature.
|
||||
|
||||
BIN
docs/en/docs/img/deployment/concepts/image01.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
106
docs/en/docs/img/deployment/concepts/process-ram.drawio
Normal file
@@ -0,0 +1,106 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="BkDNbdtn8_9fWQybnc8v" name="Page-1">
|
||||
<mxGraphModel dx="741" dy="1167" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="420" y="280" width="920" height="670" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="755" y="290" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="1110" y="410" width="190" height="500" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">RAM<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="1166.92" y="420" width="76.16" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="470" y="410" width="250" height="500" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="<font style="font-size: 24px" face="Roboto">CPU<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="554.61" y="420" width="80.77" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" source="11" target="12" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" source="11" target="13" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="820" y="525" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;endArrow=none;endFill=0;" parent="1" source="11" target="17" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="11" target="18" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="<font face="roboto"><span style="font-size: 24px">Process&nbsp;</span></font><span style="font-family: &#34;roboto&#34; ; font-size: 24px">Manager</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
|
||||
<mxGeometry x="780" y="420" width="250" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="12" target="23" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="<font face="roboto"><span style="font-size: 24px">Worker Process</span></font>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="840" y="540" width="240" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="13" target="24" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="13" target="22" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="775" y="710"/>
|
||||
<mxPoint x="775" y="688"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="<font face="roboto"><span style="font-size: 24px">Worker Process</span></font>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
|
||||
<mxGeometry x="840" y="660" width="240" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="16" target="27" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="16" target="30" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="16" value="<font face="roboto"><span style="font-size: 24px">Another Process</span></font>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="780" y="790" width="250" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="458" width="230" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="460" width="150" height="20" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="21" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#fff2cc;strokeColor=#d6b656;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="508" width="230" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="22" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="618" width="230" height="140" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="23" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">1 GB</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="490" width="150" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="24" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">1 GB</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="650" width="150" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="27" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="768" width="230" height="50" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="30" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="1130" y="810" width="150" height="50" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
59
docs/en/docs/img/deployment/concepts/process-ram.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
277
docs/en/docs/img/deployment/https/https.drawio
Normal file
@@ -0,0 +1,277 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" vertex="1" parent="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" edge="1" parent="1" target="14">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" edge="1" parent="1" target="17">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" vertex="1" parent="33">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" vertex="1" parent="33">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="101" target="32">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" vertex="1" parent="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="56" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;" edge="1" parent="1" source="55" target="49">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="58" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;startArrow=none;" edge="1" parent="1" source="102" target="57">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="410" y="400" as="targetPoint"/>
|
||||
<mxPoint x="585" y="1050" as="sourcePoint"/>
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="55" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">Cert Renovation Program</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="515" y="780" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="59" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;strokeWidth=3;startArrow=none;" edge="1" parent="1" source="103" target="55">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="875" y="1030" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="790" y="930"/>
|
||||
<mxPoint x="790" y="930"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="57" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Let's Encrypt</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="500" y="1150" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="85" target="6">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="62" target="78">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="920" y="770"/>
|
||||
<mxPoint x="920" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="65" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">Another app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: another.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="50" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="66" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">One more app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: onemore.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="180" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="78" value="<font face="Roboto"><span style="font-size: 24px ; font-weight: 400">A Database</span></font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="780" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="80" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;strokeWidth=3;endArrow=none;" edge="1" parent="1" source="57" target="103">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="480" y="1090" as="sourcePoint"/>
|
||||
<mxPoint x="875" y="1110" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="915" y="1250"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="81" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;endArrow=none;" edge="1" parent="1" source="55" target="102">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="525" y="970" as="targetPoint"/>
|
||||
<mxPoint x="550" y="880" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="525" y="930"/>
|
||||
<mxPoint x="525" y="930"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" vertex="1" parent="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="62" target="85">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="84" target="62">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" edge="1" parent="1" source="100" target="34">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" edge="1" parent="1" source="32" target="100">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="34" target="101">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="97" target="32">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="340" y="480"/>
|
||||
<mxPoint x="340" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" edge="1" parent="1" source="96" target="36">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="32" target="96">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" vertex="1" parent="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="102" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Renew HTTPS cert for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="430" y="960" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="103" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">New HTTPS cert for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="750" y="1070" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" edge="1" parent="1" source="104" target="36">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="32" target="104">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="97" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" vertex="1" parent="1">
|
||||
<mxGeometry x="90" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="36" target="97">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="415" y="680" as="sourcePoint"/>
|
||||
<mxPoint x="110" y="275" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="245" y="710"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" vertex="1" parent="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" vertex="1" parent="1">
|
||||
<mxGeometry x="885" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="111" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" edge="1" parent="1" source="6" target="84">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="850" y="390" as="sourcePoint"/>
|
||||
<mxPoint x="1190" y="700" as="targetPoint"/>
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https.svg
Normal file
|
After Width: | Height: | Size: 40 KiB |
78
docs/en/docs/img/deployment/https/https01.drawio
Normal file
@@ -0,0 +1,78 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2738" dy="2173" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-10" y="-120"/>
|
||||
<mxPoint x="-10" y="-120"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
57
docs/en/docs/img/deployment/https/https01.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
110
docs/en/docs/img/deployment/https/https02.drawio
Normal file
@@ -0,0 +1,110 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2481" dy="1867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
57
docs/en/docs/img/deployment/https/https02.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
131
docs/en/docs/img/deployment/https/https03.drawio
Normal file
@@ -0,0 +1,131 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2481" dy="1867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https03.svg
Normal file
|
After Width: | Height: | Size: 21 KiB |
152
docs/en/docs/img/deployment/https/https04.drawio
Normal file
@@ -0,0 +1,152 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="2312" dy="1667" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https04.svg
Normal file
|
After Width: | Height: | Size: 23 KiB |
166
docs/en/docs/img/deployment/https/https05.drawio
Normal file
@@ -0,0 +1,166 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="5190" dy="5090" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="640" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
<mxPoint x="-5" y="-80"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https05.svg
Normal file
|
After Width: | Height: | Size: 26 KiB |
183
docs/en/docs/img/deployment/https/https06.drawio
Normal file
@@ -0,0 +1,183 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="85" target="6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="62" target="85" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https06.svg
Normal file
|
After Width: | Height: | Size: 27 KiB |
203
docs/en/docs/img/deployment/https/https07.drawio
Normal file
@@ -0,0 +1,203 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="85" target="6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="62" target="85" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="97" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="340" y="480"/>
|
||||
<mxPoint x="340" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="97" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="90" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="36" target="97" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="415" y="680" as="sourcePoint"/>
|
||||
<mxPoint x="110" y="275" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="245" y="710"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https07.svg
Normal file
|
After Width: | Height: | Size: 29 KiB |
217
docs/en/docs/img/deployment/https/https08.drawio
Normal file
@@ -0,0 +1,217 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="jyERGzDynktFHFRGN0ph" name="Page-1">
|
||||
<mxGraphModel dx="3321" dy="2867" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="-50" width="820" height="970" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="<font face="Roboto"><span style="font-size: 24px">Server(s)</span></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="-50" width="300" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;exitX=0.092;exitY=1.01;exitDx=0;exitDy=0;dashed=1;exitPerimeter=0;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="521"/>
|
||||
<mxPoint x="800" y="560"/>
|
||||
</Array>
|
||||
<mxPoint x="803" y="521" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=3;dashed=1;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="800" y="520" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="800" y="680"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="-140" y="-75" width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">https://someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;" parent="33" vertex="1">
|
||||
<mxGeometry x="60" y="27" width="380" height="250" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;strokeColor=none;" parent="33" vertex="1">
|
||||
<mxGeometry width="500" height="350" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="101" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="390" y="-190" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-132"/>
|
||||
<mxPoint x="280" y="-132"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">DNS Servers</font>" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-60" y="-540" width="330" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="495" y="320" width="355" height="440" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="<font style="font-size: 24px" face="Roboto">TLS Termination Proxy<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="525" y="330" width="280" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="85" target="6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;entryX=0.073;entryY=0.01;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.075;exitY=0.998;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="62" target="78" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="917" y="754" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="62" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">FastAPI</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal"> app for: someapp.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="650" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="65" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">Another app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: another.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="50" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="66" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">One more app</font><font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px ; font-weight: normal">: onemore.example.com</font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="180" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="78" value="<font face="Roboto"><span style="font-size: 24px ; font-weight: 400">A Database</span></font>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="895" y="780" width="300" height="100" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Plain response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="62" target="85" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1030.0000000000005" y="649.9999999999995" as="sourcePoint"/>
|
||||
<mxPoint x="850" y="540.0000000000005" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="1030" y="540"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="6" target="62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="1240" y="390"/>
|
||||
<mxPoint x="1240" y="700"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Decrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="890" y="350" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="100" target="34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="65.05882352941171" y="-220" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;dashed=1;" parent="1" source="32" target="100" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="110" y="-75" as="sourcePoint"/>
|
||||
<mxPoint x="-4.941176470588289" y="-139.99999999999955" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
<mxPoint x="-5" y="-90"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="34" target="101" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="105" y="-280" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="-260" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="390" y="-430"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="97" target="32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="340" y="480"/>
|
||||
<mxPoint x="340" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto" style="font-size: 24px">Port 443 (HTTPS)</font>" style="ellipse;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="680" width="170" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=classic;endFill=1;strokeWidth=3;" parent="1" source="96" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="50" y="500" as="sourcePoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="50" y="740"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="96" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="55" y="330" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="340"/>
|
||||
<mxPoint x="160" y="340"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted request for: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#82b366;strokeWidth=3;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="-10" y="400" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Who is: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="-210" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="101" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">IP:</span><br style="font-family: &#34;roboto&#34;"><span style="font-family: &#34;roboto&#34; ; font-size: 24px">123.124.125.126</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="270" y="-290" width="240" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=0;strokeWidth=3;" parent="1" source="104" target="36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="770"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="104" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">TLS Handshake</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="300" width="230" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="32" target="104" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="-40" y="275" as="sourcePoint"/>
|
||||
<mxPoint x="341.38784067832285" y="770" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="-40" y="290"/>
|
||||
<mxPoint x="-40" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="97" value="<span style="font-family: &#34;roboto&#34; ; font-size: 24px">Encrypted response from: someapp.example.com</span>" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;strokeColor=#9673a6;strokeWidth=3;fillColor=#e1d5e7;" parent="1" vertex="1">
|
||||
<mxGeometry x="90" y="500" width="310" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;startArrow=none;startFill=0;endArrow=none;endFill=1;strokeWidth=3;" parent="1" source="36" target="97" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="415" y="680" as="sourcePoint"/>
|
||||
<mxPoint x="110" y="275" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="245" y="710"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;strokeWidth=4;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
<mxGeometry x="510" y="400" width="310" height="320" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="50" value="<font style="font-size: 24px" face="Roboto">HTTPS certificates<br></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeWidth=3;fontFamily=Roboto Mono, mono;FType=g;" parent="1" vertex="1">
|
||||
<mxGeometry x="550.9" y="410" width="228.21" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="51" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">someapp.example.com</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="465" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="52" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">another.example.net</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="545" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="53" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">onemore.example.org</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#666666;strokeWidth=3;fillColor=#f5f5f5;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="530" y="625" width="270" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="42" value="<font face="Roboto" data-font-src="https://fonts.googleapis.com/css?family=Roboto"><span style="font-size: 24px">IP:</span><br><span style="font-size: 24px">123.124.125.126</span><br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#000000;strokeWidth=3;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="600" width="220" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
62
docs/en/docs/img/deployment/https/https08.svg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/en/docs/img/sponsors/calmcode.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
192
docs/en/docs/img/sponsors/haystack-fastapi.svg
Normal file
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="240"
|
||||
height="100"
|
||||
viewBox="0 0 240 100"
|
||||
version="1.1"
|
||||
id="svg5">
|
||||
<metadata
|
||||
id="metadata44">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="14.514768"
|
||||
y="110.37975"
|
||||
width="255.52743"
|
||||
height="29.367089"
|
||||
id="rect25873" />
|
||||
<style
|
||||
id="style51">.cls-1{fill:#03af9d;}.cls-2{fill:#fff;}.cls-3{fill:#2b2f55;}.cls-4,.cls-5{fill:#a0a0c0;}.cls-5{fill-opacity:0.3;}</style>
|
||||
</defs>
|
||||
<g
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke-width:7.0842;stop-color:#000000"
|
||||
id="rect869"
|
||||
width="240"
|
||||
height="100"
|
||||
x="0"
|
||||
y="0" />
|
||||
<g
|
||||
id="g77"
|
||||
transform="matrix(1.21,0,0,1.21,-32.437213,-33.51152)">
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M 40.56,37 H 70.44 A 3.56,3.56 0 0 1 74,40.56 V 80.44 A 3.56,3.56 0 0 1 70.44,84 H 40.56 A 3.56,3.56 0 0 1 37,80.44 V 40.56 A 3.56,3.56 0 0 1 40.56,37 Z"
|
||||
id="path55" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 44,55.52 a 11.5,11.5 0 1 1 23,0 v 17 A 0.5,0.5 0 0 1 66.5,73.02 4.5,4.5 0 0 1 62,68.5 v -13 a 6.5,6.5 0 1 0 -13,0 v 5 a 0.49,0.49 0 0 0 0.49,0.5 h 3 A 0.5,0.5 0 0 0 53,60.5 v -4 a 2.5,2.5 0 0 1 5,0 v 20 A 0.51,0.51 0 0 1 57.48,77 4.48,4.48 0 0 1 53,72.5 v -6 A 0.51,0.51 0 0 0 52.49,66 h -3 A 0.49,0.49 0 0 0 49,66.5 v 2 A 4.5,4.5 0 0 1 44.5,73 0.5,0.5 0 0 1 44,72.5 Z"
|
||||
id="path57" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 98.91,48.18 a 4.9,4.9 0 0 0 -4.53,2.63 v -8.69 a 0.43,0.43 0 0 0 -0.46,-0.46 h -3.3 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 h 3.3 a 0.43,0.43 0 0 0 0.46,-0.46 v -7.81 c 0,-2.11 1.16,-3.24 2.94,-3.24 1.78,0 2.78,1.13 2.78,3.24 v 7.77 a 0.44,0.44 0 0 0 0.46,0.46 h 3.28 a 0.46,0.46 0 0 0 0.49,-0.46 v -8.72 c 0,-3.92 -2.33,-6.18 -5.42,-6.18 z m 14.78,15.73 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 118.69,49 l -0.06,1.65 a 5.49,5.49 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.87,7.88 7.09,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.79,4 z m 17,9.3 a 0.56,0.56 0 0 0 0.55,-0.36 l 7.92,-19.92 c 0.13,-0.34 0,-0.52 -0.36,-0.52 h -3.37 a 0.56,0.56 0 0 0 -0.55,0.36 l -3.55,9.73 -3.58,-9.73 a 0.56,0.56 0 0 0 -0.55,-0.36 h -3.37 c -0.33,0 -0.49,0.18 -0.36,0.52 l 5.78,14.5 -2.14,5.26 c -0.16,0.34 0,0.52 0.36,0.52 z m 15.09,-5.44 c 3,0 5.6,-1.56 5.63,-4.59 0,-2.3 -1.47,-3.58 -3.36,-4.35 l -2.12,-0.82 c -0.82,-0.31 -1.43,-0.74 -1.43,-1.47 0,-0.73 0.45,-1.2 1.43,-1.2 a 4.3,4.3 0 0 1 2.73,1.26 c 0.27,0.21 0.49,0.24 0.7,0 l 1.35,-1.62 a 0.49,0.49 0 0 0 0,-0.65 6.44,6.44 0 0 0 -5,-2.29 c -2.78,0 -5.23,1.65 -5.23,4.53 0,2.08 1.34,3.33 3.27,4.1 l 1.9,0.76 c 1.1,0.46 1.56,0.83 1.56,1.56 0,0.73 -0.67,1.26 -1.68,1.26 a 5.7,5.7 0 0 1 -3.42,-1.47 0.47,0.47 0 0 0 -0.77,0.12 l -1.07,1.5 a 0.76,0.76 0 0 0 -0.06,0.89 7.16,7.16 0 0 0 5.62,2.48 z m 14,0 c 1,0 2.54,-0.19 2.54,-0.89 v -2.23 c 0,-0.31 -0.22,-0.46 -0.55,-0.43 -0.33,0.03 -0.68,0 -0.95,0 a 1.2,1.2 0 0 1 -1.32,-1.32 v -7.22 h 2.36 a 0.44,0.44 0 0 0 0.46,-0.46 V 49 a 0.44,0.44 0 0 0 -0.46,-0.46 h -2.36 V 45 a 0.43,0.43 0 0 0 -0.46,-0.46 H 156.6 A 0.43,0.43 0 0 0 156.14,45 v 3.52 h -1.9 a 0.43,0.43 0 0 0 -0.46,0.46 v 2.35 a 0.43,0.43 0 0 0 0.46,0.46 h 1.9 v 7.71 c 0.06,3.31 2.2,4.41 4.59,4.41 z m 11.45,0 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 177.12,49 l -0.06,1.65 a 5.48,5.48 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.97,7.88 7.15,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.73,4 z m 18.79,3.86 a 7.17,7.17 0 0 0 5.76,-2.7 0.44,0.44 0 0 0 -0.07,-0.64 l -1.83,-1.8 a 0.54,0.54 0 0 0 -0.8,0 3.85,3.85 0 0 1 -2.81,1.23 3.9,3.9 0 0 1 -4,-4 3.78,3.78 0 0 1 3.95,-3.93 3.85,3.85 0 0 1 2.94,1.25 0.56,0.56 0 0 0 0.8,0 l 1.83,-1.81 a 0.43,0.43 0 0 0 0.06,-0.64 7.19,7.19 0 0 0 -5.81,-2.69 7.63,7.63 0 0 0 -7.83,7.8 7.74,7.74 0 0 0 7.87,7.93 z m 12.5,-0.37 a 0.43,0.43 0 0 0 0.46,-0.46 V 59.9 l 1.87,-2.11 4,5.47 a 0.57,0.57 0 0 0 0.55,0.28 h 3.55 c 0.37,0 0.49,-0.24 0.27,-0.55 l -5.87,-8.11 5.08,-5.78 c 0.24,-0.31 0.15,-0.55 -0.25,-0.55 h -3.66 a 0.67,0.67 0 0 0 -0.55,0.24 l -4.93,5.81 V 42.12 a 0.43,0.43 0 0 0 -0.46,-0.46 H 201 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 z"
|
||||
id="path59" />
|
||||
<path
|
||||
class="cls-3"
|
||||
d="m 98.91,48.18 a 4.9,4.9 0 0 0 -4.53,2.63 v -8.69 a 0.43,0.43 0 0 0 -0.46,-0.46 h -3.3 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 h 3.3 a 0.43,0.43 0 0 0 0.46,-0.46 v -7.81 c 0,-2.11 1.16,-3.24 2.94,-3.24 1.78,0 2.78,1.13 2.78,3.24 v 7.77 a 0.44,0.44 0 0 0 0.46,0.46 h 3.28 a 0.46,0.46 0 0 0 0.49,-0.46 v -8.72 c 0,-3.92 -2.33,-6.18 -5.42,-6.18 z m 14.78,15.73 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 118.69,49 l -0.06,1.65 a 5.49,5.49 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.87,7.88 7.09,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.79,4 z m 17,9.3 a 0.56,0.56 0 0 0 0.55,-0.36 l 7.92,-19.92 c 0.13,-0.34 0,-0.52 -0.36,-0.52 h -3.37 a 0.56,0.56 0 0 0 -0.55,0.36 l -3.55,9.73 -3.58,-9.73 a 0.56,0.56 0 0 0 -0.55,-0.36 h -3.37 c -0.33,0 -0.49,0.18 -0.36,0.52 l 5.78,14.5 -2.14,5.26 c -0.16,0.34 0,0.52 0.36,0.52 z m 15.09,-5.44 c 3,0 5.6,-1.56 5.63,-4.59 0,-2.3 -1.47,-3.58 -3.36,-4.35 l -2.12,-0.82 c -0.82,-0.31 -1.43,-0.74 -1.43,-1.47 0,-0.73 0.45,-1.2 1.43,-1.2 a 4.3,4.3 0 0 1 2.73,1.26 c 0.27,0.21 0.49,0.24 0.7,0 l 1.35,-1.62 a 0.49,0.49 0 0 0 0,-0.65 6.44,6.44 0 0 0 -5,-2.29 c -2.78,0 -5.23,1.65 -5.23,4.53 0,2.08 1.34,3.33 3.27,4.1 l 1.9,0.76 c 1.1,0.46 1.56,0.83 1.56,1.56 0,0.73 -0.67,1.26 -1.68,1.26 a 5.7,5.7 0 0 1 -3.42,-1.47 0.47,0.47 0 0 0 -0.77,0.12 l -1.07,1.5 a 0.76,0.76 0 0 0 -0.06,0.89 7.16,7.16 0 0 0 5.62,2.48 z m 14,0 c 1,0 2.54,-0.19 2.54,-0.89 v -2.23 c 0,-0.31 -0.22,-0.46 -0.55,-0.43 -0.33,0.03 -0.68,0 -0.95,0 a 1.2,1.2 0 0 1 -1.32,-1.32 v -7.22 h 2.36 a 0.44,0.44 0 0 0 0.46,-0.46 V 49 a 0.44,0.44 0 0 0 -0.46,-0.46 h -2.36 V 45 a 0.43,0.43 0 0 0 -0.46,-0.46 H 156.6 A 0.43,0.43 0 0 0 156.14,45 v 3.52 h -1.9 a 0.43,0.43 0 0 0 -0.46,0.46 v 2.35 a 0.43,0.43 0 0 0 0.46,0.46 h 1.9 v 7.71 c 0.06,3.31 2.2,4.41 4.59,4.41 z m 11.45,0 a 5.63,5.63 0 0 0 4.9,-2.48 l 0.06,1.65 a 0.43,0.43 0 0 0 0.46,0.46 h 3 a 0.45,0.45 0 0 0 0.49,-0.46 V 49 a 0.43,0.43 0 0 0 -0.45,-0.46 h -3 A 0.43,0.43 0 0 0 177.12,49 l -0.06,1.65 a 5.48,5.48 0 0 0 -4.9,-2.48 c -4.22,0 -7.13,3.43 -7.13,7.86 0,4.43 2.97,7.88 7.15,7.88 z m 0.89,-3.86 a 4,4 0 0 1 0,-8 3.71,3.71 0 0 1 3.79,4 3.7,3.7 0 0 1 -3.73,4 z m 18.79,3.86 a 7.17,7.17 0 0 0 5.76,-2.7 0.44,0.44 0 0 0 -0.07,-0.64 l -1.83,-1.8 a 0.54,0.54 0 0 0 -0.8,0 3.85,3.85 0 0 1 -2.81,1.23 3.9,3.9 0 0 1 -4,-4 3.78,3.78 0 0 1 3.95,-3.93 3.85,3.85 0 0 1 2.94,1.25 0.56,0.56 0 0 0 0.8,0 l 1.83,-1.81 a 0.43,0.43 0 0 0 0.06,-0.64 7.19,7.19 0 0 0 -5.81,-2.69 7.63,7.63 0 0 0 -7.83,7.8 7.74,7.74 0 0 0 7.87,7.93 z m 12.5,-0.37 a 0.43,0.43 0 0 0 0.46,-0.46 V 59.9 l 1.87,-2.11 4,5.47 a 0.57,0.57 0 0 0 0.55,0.28 h 3.55 c 0.37,0 0.49,-0.24 0.27,-0.55 l -5.87,-8.11 5.08,-5.78 c 0.24,-0.31 0.15,-0.55 -0.25,-0.55 h -3.66 a 0.67,0.67 0 0 0 -0.55,0.24 l -4.93,5.81 V 42.12 a 0.43,0.43 0 0 0 -0.46,-0.46 H 201 a 0.43,0.43 0 0 0 -0.46,0.46 v 21 a 0.43,0.43 0 0 0 0.46,0.46 z"
|
||||
id="path61" />
|
||||
<path
|
||||
class="cls-4"
|
||||
d="m 94.88,81.26 a 3.88,3.88 0 0 0 0,-7.71 2.7,2.7 0 0 0 -2.28,1 v -4 a 0.21,0.21 0 0 0 -0.22,-0.23 h -1.64 a 0.21,0.21 0 0 0 -0.22,0.23 v 10.3 a 0.22,0.22 0 0 0 0.24,0.23 h 1.47 a 0.21,0.21 0 0 0 0.22,-0.23 v -0.81 a 2.74,2.74 0 0 0 2.43,1.22 z m -0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.9,4.56 a 0.27,0.27 0 0 0 0.27,-0.18 L 106.51,74 c 0.06,-0.16 0,-0.25 -0.18,-0.25 h -1.65 a 0.27,0.27 0 0 0 -0.27,0.18 l -1.74,4.77 -1.76,-4.77 a 0.27,0.27 0 0 0 -0.27,-0.18 H 99 c -0.16,0 -0.24,0.09 -0.18,0.25 l 2.84,7.11 -1.07,2.58 c -0.07,0.17 0,0.26 0.18,0.26 z M 113.9,81.26 A 2.76,2.76 0 0 0 116.3,80 v 0.81 a 0.21,0.21 0 0 0 0.22,0.23 H 118 a 0.23,0.23 0 0 0 0.24,-0.23 V 70.58 a 0.22,0.22 0 0 0 -0.23,-0.23 h -1.63 a 0.22,0.22 0 0 0 -0.23,0.23 v 4 a 2.65,2.65 0 0 0 -2.26,-1 3.87,3.87 0 0 0 0,7.71 z m 0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 9.37,1.89 a 3.61,3.61 0 0 0 2.6,-0.93 c 0.14,-0.12 0.16,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.16,0.16 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.17,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.04,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 10.3,4.53 a 3.65,3.65 0 0 0 2.61,-0.93 c 0.13,-0.12 0.15,-0.23 0.07,-0.33 l -0.55,-0.77 a 0.18,0.18 0 0 0 -0.26,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.16,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.75,3.82 3.81,3.81 0 0 0 4,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 8.56,7.2 a 0.21,0.21 0 0 0 0.22,-0.23 v -3.49 a 2.76,2.76 0 0 0 2.22,1.05 3.88,3.88 0 0 0 0,-7.71 2.69,2.69 0 0 0 -2.4,1.21 v -0.81 a 0.21,0.21 0 0 0 -0.22,-0.22 h -1.48 a 0.21,0.21 0 0 0 -0.22,0.22 v 9.75 a 0.21,0.21 0 0 0 0.22,0.23 z m 2.07,-4.56 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.68,1.89 c 1.47,0 2.75,-0.77 2.76,-2.25 a 2.22,2.22 0 0 0 -1.65,-2.13 l -1,-0.41 c -0.41,-0.15 -0.71,-0.36 -0.71,-0.72 0,-0.36 0.23,-0.58 0.71,-0.58 a 2.12,2.12 0 0 1 1.33,0.61 c 0.14,0.11 0.24,0.12 0.35,0 l 0.66,-0.79 a 0.24,0.24 0 0 0 0,-0.32 3.16,3.16 0 0 0 -2.46,-1.12 2.32,2.32 0 0 0 -2.56,2.22 2.15,2.15 0 0 0 1.6,2 l 0.93,0.37 c 0.54,0.23 0.77,0.41 0.77,0.77 0,0.36 -0.33,0.61 -0.83,0.61 a 2.76,2.76 0 0 1 -1.69,-0.72 0.23,0.23 0 0 0 -0.38,0.06 l -0.52,0.74 a 0.35,0.35 0 0 0 0,0.43 3.5,3.5 0 0 0 2.67,1.23 z m 7.82,0 a 3.63,3.63 0 0 0 2.61,-0.93 c 0.14,-0.12 0.15,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.17,0.17 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.18,-1.44 h 4.47 c 0.65,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.52,-3.51 3.66,3.66 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.01,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.67,1.47 z m 9.48,4.53 c 0.5,0 1.25,-0.09 1.25,-0.44 v -1.09 c 0,-0.15 -0.11,-0.23 -0.27,-0.21 h -0.47 a 0.58,0.58 0 0 1 -0.64,-0.64 v -3.55 h 1.15 a 0.21,0.21 0 0 0 0.23,-0.22 V 74 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.15 V 72 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.63 A 0.21,0.21 0 0 0 161.39,72 v 1.73 h -0.93 a 0.21,0.21 0 0 0 -0.22,0.22 v 1.16 a 0.21,0.21 0 0 0 0.22,0.22 h 0.93 v 3.78 a 2,2 0 0 0 2.16,2.15 z"
|
||||
id="path63" />
|
||||
<path
|
||||
class="cls-5"
|
||||
d="m 94.88,81.26 a 3.88,3.88 0 0 0 0,-7.71 2.7,2.7 0 0 0 -2.28,1 v -4 a 0.21,0.21 0 0 0 -0.22,-0.23 h -1.64 a 0.21,0.21 0 0 0 -0.22,0.23 v 10.3 a 0.22,0.22 0 0 0 0.24,0.23 h 1.47 a 0.21,0.21 0 0 0 0.22,-0.23 v -0.81 a 2.74,2.74 0 0 0 2.43,1.22 z m -0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.9,4.56 a 0.27,0.27 0 0 0 0.27,-0.18 L 106.51,74 c 0.06,-0.16 0,-0.25 -0.18,-0.25 h -1.65 a 0.27,0.27 0 0 0 -0.27,0.18 l -1.74,4.77 -1.76,-4.77 a 0.27,0.27 0 0 0 -0.27,-0.18 H 99 c -0.16,0 -0.24,0.09 -0.18,0.25 l 2.84,7.11 -1.07,2.58 c -0.07,0.17 0,0.26 0.18,0.26 z M 113.9,81.26 A 2.76,2.76 0 0 0 116.3,80 v 0.81 a 0.21,0.21 0 0 0 0.22,0.23 H 118 a 0.23,0.23 0 0 0 0.24,-0.23 V 70.58 a 0.22,0.22 0 0 0 -0.23,-0.23 h -1.63 a 0.22,0.22 0 0 0 -0.23,0.23 v 4 a 2.65,2.65 0 0 0 -2.26,-1 3.87,3.87 0 0 0 0,7.71 z m 0.43,-1.89 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 9.37,1.89 a 3.61,3.61 0 0 0 2.6,-0.93 c 0.14,-0.12 0.16,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.16,0.16 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.17,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.04,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 10.3,4.53 a 3.65,3.65 0 0 0 2.61,-0.93 c 0.13,-0.12 0.15,-0.23 0.07,-0.33 l -0.55,-0.77 a 0.18,0.18 0 0 0 -0.26,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.16,-1.44 h 4.47 c 0.64,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.53,-3.51 3.67,3.67 0 0 0 -3.75,3.82 3.81,3.81 0 0 0 4,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.66,1.47 z m 8.56,7.2 a 0.21,0.21 0 0 0 0.22,-0.23 v -3.49 a 2.76,2.76 0 0 0 2.22,1.05 3.88,3.88 0 0 0 0,-7.71 2.69,2.69 0 0 0 -2.4,1.21 v -0.81 a 0.21,0.21 0 0 0 -0.22,-0.22 h -1.48 a 0.21,0.21 0 0 0 -0.22,0.22 v 9.75 a 0.21,0.21 0 0 0 0.22,0.23 z m 2.07,-4.56 a 2,2 0 0 1 0,-3.93 2,2 0 0 1 0,3.93 z m 7.68,1.89 c 1.47,0 2.75,-0.77 2.76,-2.25 a 2.22,2.22 0 0 0 -1.65,-2.13 l -1,-0.41 c -0.41,-0.15 -0.71,-0.36 -0.71,-0.72 0,-0.36 0.23,-0.58 0.71,-0.58 a 2.12,2.12 0 0 1 1.33,0.61 c 0.14,0.11 0.24,0.12 0.35,0 l 0.66,-0.79 a 0.24,0.24 0 0 0 0,-0.32 3.16,3.16 0 0 0 -2.46,-1.12 2.32,2.32 0 0 0 -2.56,2.22 2.15,2.15 0 0 0 1.6,2 l 0.93,0.37 c 0.54,0.23 0.77,0.41 0.77,0.77 0,0.36 -0.33,0.61 -0.83,0.61 a 2.76,2.76 0 0 1 -1.69,-0.72 0.23,0.23 0 0 0 -0.38,0.06 l -0.52,0.74 a 0.35,0.35 0 0 0 0,0.43 3.5,3.5 0 0 0 2.67,1.23 z m 7.82,0 a 3.63,3.63 0 0 0 2.61,-0.93 c 0.14,-0.12 0.15,-0.23 0.08,-0.33 l -0.56,-0.77 a 0.17,0.17 0 0 0 -0.25,-0.06 3.2,3.2 0 0 1 -1.71,0.45 2,2 0 0 1 -2.18,-1.44 h 4.47 c 0.65,0 0.81,-0.4 0.81,-1.12 a 3.39,3.39 0 0 0 -3.52,-3.51 3.66,3.66 0 0 0 -3.78,3.82 3.81,3.81 0 0 0 4.01,3.89 z m -2,-4.53 a 1.7,1.7 0 0 1 1.8,-1.47 1.56,1.56 0 0 1 1.67,1.47 z m 9.48,4.53 c 0.5,0 1.25,-0.09 1.25,-0.44 v -1.09 c 0,-0.15 -0.11,-0.23 -0.27,-0.21 h -0.47 a 0.58,0.58 0 0 1 -0.64,-0.64 v -3.55 h 1.15 a 0.21,0.21 0 0 0 0.23,-0.22 V 74 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.15 V 72 a 0.21,0.21 0 0 0 -0.23,-0.22 h -1.63 A 0.21,0.21 0 0 0 161.39,72 v 1.73 h -0.93 a 0.21,0.21 0 0 0 -0.22,0.22 v 1.16 a 0.21,0.21 0 0 0 0.22,0.22 h 0.93 v 3.78 a 2,2 0 0 0 2.16,2.15 z"
|
||||
id="path65" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="Smarter search with NLP + FastAPI"
|
||||
transform="translate(-3.3488372,-37.07907)"
|
||||
id="text25871"
|
||||
style="font-size:14px;line-height:1.25;white-space:pre;shape-inside:url(#rect25873);fill:#2b2f55">
|
||||
<path
|
||||
d="m 18.379625,123.61004 c 1.666,0 3.276,-0.91 3.276,-2.884 0,-1.652 -1.12,-2.31 -2.184,-2.814 l -1.008,-0.462 c -0.756,-0.35 -1.148,-0.574 -1.148,-1.148 0,-0.588 0.448,-0.938 1.162,-0.938 0.616,0 1.106,0.266 1.708,0.798 0.112,0.084 0.224,0.056 0.308,-0.056 l 0.798,-0.966 c 0.084,-0.112 0.084,-0.224 -0.014,-0.322 -0.77,-0.826 -1.708,-1.288 -2.87,-1.288 -1.666,0 -3.024,0.98 -3.024,2.758 0,1.456 0.924,2.198 1.96,2.674 l 1.19,0.56 c 0.686,0.308 1.176,0.546 1.176,1.204 0,0.7 -0.546,1.05 -1.372,1.05 -0.798,0 -1.358,-0.336 -1.974,-0.966 -0.112,-0.098 -0.224,-0.084 -0.322,0.028 l -0.812,0.938 c -0.098,0.112 -0.098,0.21 -0.028,0.308 0.574,0.784 1.652,1.526 3.178,1.526 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58197" />
|
||||
<path
|
||||
d="m 24.82925,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.598 c 0,-0.938 0.42,-1.442 1.148,-1.442 0.742,0 1.092,0.504 1.092,1.442 v 3.598 c 0,0.126 0.084,0.21 0.224,0.21 h 1.498 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.598 c 0,-0.938 0.42,-1.442 1.148,-1.442 0.756,0 1.092,0.504 1.092,1.442 v 3.598 c 0,0.126 0.084,0.21 0.21,0.21 h 1.512 c 0.126,0 0.21,-0.084 0.21,-0.21 v -4.018 c 0,-1.792 -0.896,-2.8 -2.366,-2.8 -1.064,0 -1.736,0.518 -2.114,1.218 -0.378,-0.798 -1.064,-1.218 -1.946,-1.218 -0.938,0 -1.554,0.476 -1.932,1.12 l -0.042,-0.728 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58199" />
|
||||
<path
|
||||
d="m 37.941125,123.61004 c 1.022,0 1.764,-0.434 2.24,-1.134 l 0.028,0.756 c 0,0.126 0.084,0.21 0.21,0.21 h 1.358 c 0.126,0 0.224,-0.084 0.224,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.372 c -0.126,0 -0.21,0.084 -0.21,0.21 l -0.028,0.756 c -0.462,-0.714 -1.204,-1.134 -2.24,-1.134 -1.932,0 -3.262,1.568 -3.262,3.598 0,2.044 1.33,3.598 3.262,3.598 z m 0.406,-1.764 c -1.022,0 -1.778,-0.728 -1.778,-1.834 0,-1.092 0.756,-1.834 1.778,-1.834 1.022,0 1.736,0.728 1.736,1.834 0,1.106 -0.714,1.834 -1.736,1.834 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58201" />
|
||||
<path
|
||||
d="m 45.39175,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.43 c 0,-0.98 0.504,-1.638 1.456,-1.638 0.196,0 0.35,0.028 0.504,0.07 0.182,0.042 0.28,0 0.28,-0.168 v -1.204 c 0,-0.126 -0.028,-0.21 -0.126,-0.28 -0.112,-0.084 -0.308,-0.168 -0.63,-0.168 -0.812,0 -1.246,0.546 -1.484,1.274 l -0.056,-0.882 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58203" />
|
||||
<path
|
||||
d="m 51.950531,123.61004 c 0.462,0 1.162,-0.084 1.162,-0.406 v -1.022 c 0,-0.14 -0.098,-0.21 -0.252,-0.196 -0.168,0.014 -0.308,0.014 -0.434,0.014 -0.364,0 -0.602,-0.196 -0.602,-0.602 v -3.318 h 1.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.078 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.078 v -1.61 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.61 h -0.868 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.078 c 0,0.126 0.084,0.21 0.21,0.21 h 0.868 v 3.528 c 0,1.498 0.98,2.002 2.072,2.002 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58205" />
|
||||
<path
|
||||
d="m 57.667781,123.61004 c 0.966,0 1.848,-0.28 2.436,-0.868 0.126,-0.112 0.14,-0.21 0.07,-0.308 l -0.518,-0.714 c -0.07,-0.098 -0.14,-0.112 -0.238,-0.056 -0.56,0.322 -1.064,0.42 -1.596,0.42 -1.106,0 -1.82,-0.476 -2.03,-1.344 h 4.172 c 0.602,0 0.756,-0.378 0.756,-1.05 0,-1.736 -1.204,-3.276 -3.29,-3.276 -2.114,0 -3.528,1.554 -3.528,3.57 0,2.072 1.512,3.626 3.766,3.626 z m -1.904,-4.228 c 0.168,-0.91 0.826,-1.372 1.68,-1.372 0.826,0 1.442,0.462 1.554,1.372 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58207" />
|
||||
<path
|
||||
d="m 63.739406,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.43 c 0,-0.98 0.504,-1.638 1.456,-1.638 0.196,0 0.35,0.028 0.504,0.07 0.182,0.042 0.28,0 0.28,-0.168 v -1.204 c 0,-0.126 -0.028,-0.21 -0.126,-0.28 -0.112,-0.084 -0.308,-0.168 -0.63,-0.168 -0.812,0 -1.246,0.546 -1.484,1.274 l -0.056,-0.882 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58209" />
|
||||
<path
|
||||
d="m 72.367125,123.61004 c 1.372,0 2.562,-0.714 2.576,-2.1 0.014,-1.05 -0.672,-1.638 -1.54,-1.988 l -0.966,-0.378 c -0.378,-0.14 -0.658,-0.336 -0.658,-0.672 0,-0.294 0.21,-0.546 0.658,-0.546 0.406,0 0.798,0.196 1.246,0.574 0.126,0.098 0.224,0.112 0.322,0 l 0.616,-0.742 c 0.07,-0.084 0.098,-0.196 0.014,-0.294 -0.574,-0.686 -1.4,-1.05 -2.296,-1.05 -1.274,0 -2.394,0.756 -2.394,2.072 0,0.952 0.616,1.526 1.498,1.876 l 0.868,0.35 c 0.504,0.21 0.714,0.378 0.714,0.714 0,0.392 -0.308,0.574 -0.77,0.574 -0.532,0 -1.008,-0.238 -1.582,-0.672 -0.112,-0.084 -0.238,-0.112 -0.35,0.056 l -0.49,0.686 c -0.098,0.154 -0.112,0.308 -0.028,0.406 0.546,0.644 1.414,1.134 2.562,1.134 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58211" />
|
||||
<path
|
||||
d="m 79.652156,123.61004 c 0.966,0 1.848,-0.28 2.436,-0.868 0.126,-0.112 0.14,-0.21 0.07,-0.308 l -0.518,-0.714 c -0.07,-0.098 -0.14,-0.112 -0.238,-0.056 -0.56,0.322 -1.064,0.42 -1.596,0.42 -1.106,0 -1.82,-0.476 -2.03,-1.344 h 4.172 c 0.602,0 0.756,-0.378 0.756,-1.05 0,-1.736 -1.204,-3.276 -3.29,-3.276 -2.114,0 -3.528,1.554 -3.528,3.57 0,2.072 1.512,3.626 3.766,3.626 z m -1.904,-4.228 c 0.168,-0.91 0.826,-1.372 1.68,-1.372 0.826,0 1.442,0.462 1.554,1.372 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58213" />
|
||||
<path
|
||||
d="m 86.913781,123.61004 c 1.022,0 1.764,-0.434 2.24,-1.134 l 0.028,0.756 c 0,0.126 0.084,0.21 0.21,0.21 h 1.358 c 0.126,0 0.224,-0.084 0.224,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.372 c -0.126,0 -0.21,0.084 -0.21,0.21 l -0.028,0.756 c -0.462,-0.714 -1.204,-1.134 -2.24,-1.134 -1.932,0 -3.262,1.568 -3.262,3.598 0,2.044 1.33,3.598 3.262,3.598 z m 0.406,-1.764 c -1.022,0 -1.778,-0.728 -1.778,-1.834 0,-1.092 0.756,-1.834 1.778,-1.834 1.022,0 1.736,0.728 1.736,1.834 0,1.106 -0.714,1.834 -1.736,1.834 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58215" />
|
||||
<path
|
||||
d="m 94.364406,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.43 c 0,-0.98 0.504,-1.638 1.456,-1.638 0.196,0 0.35,0.028 0.504,0.07 0.182,0.042 0.28,0 0.28,-0.168 v -1.204 c 0,-0.126 -0.028,-0.21 -0.126,-0.28 -0.112,-0.084 -0.308,-0.168 -0.63,-0.168 -0.812,0 -1.246,0.546 -1.484,1.274 l -0.056,-0.882 c 0,-0.168 -0.084,-0.224 -0.21,-0.224 h -1.456 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58217" />
|
||||
<path
|
||||
d="m 101.14106,123.61004 c 1.106,0 2.044,-0.476 2.632,-1.232 0.084,-0.098 0.056,-0.21 -0.028,-0.294 l -0.84,-0.826 c -0.112,-0.112 -0.266,-0.098 -0.364,0 -0.378,0.378 -0.784,0.574 -1.316,0.574 -1.134,0 -1.847998,-0.84 -1.847998,-1.848 0,-1.008 0.713998,-1.792 1.805998,-1.792 0.56,0 0.966,0.196 1.344,0.574 0.098,0.098 0.252,0.112 0.364,0 l 0.84,-0.826 c 0.084,-0.084 0.112,-0.196 0.028,-0.294 -0.588,-0.756 -1.526,-1.232 -2.66,-1.232 -2.057998,0 -3.583998,1.54 -3.583998,3.57 0,2.058 1.540001,3.626 3.625998,3.626 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58219" />
|
||||
<path
|
||||
d="m 109.1425,116.41404 c -1.022,0 -1.68,0.504 -2.072,1.204 v -3.976 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.512 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.59 c 0,0.126 0.084,0.21 0.21,0.21 h 1.512 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.556 c 0,-0.966 0.532,-1.484 1.344,-1.484 0.826,0 1.274,0.518 1.274,1.484 v 3.556 c 0,0.126 0.084,0.21 0.21,0.21 h 1.498 c 0.126,0 0.224,-0.084 0.224,-0.21 v -3.99 c 0,-1.792 -1.05,-2.828 -2.478,-2.828 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58221" />
|
||||
<path
|
||||
d="m 119.54997,123.44204 c 0.112,0 0.21,-0.056 0.252,-0.168 l 1.526,-4.452 1.54,4.452 c 0.042,0.112 0.14,0.168 0.252,0.168 h 1.554 c 0.112,0 0.21,-0.056 0.252,-0.168 l 2.142,-6.454 c 0.056,-0.154 -0.014,-0.238 -0.168,-0.238 h -1.456 c -0.112,0 -0.224,0.056 -0.266,0.168 l -1.372,4.452 -1.484,-4.452 c -0.042,-0.112 -0.14,-0.168 -0.252,-0.168 h -1.47 c -0.112,0 -0.21,0.056 -0.252,0.168 l -1.498,4.452 -1.372,-4.452 c -0.042,-0.112 -0.14,-0.168 -0.252,-0.168 h -1.47 c -0.154,0 -0.224,0.084 -0.168,0.238 l 2.156,6.454 c 0.042,0.112 0.14,0.168 0.252,0.168 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58223" />
|
||||
<path
|
||||
d="m 129.32,115.36404 c 0.616,0 1.064,-0.476 1.064,-1.078 0,-0.588 -0.448,-1.064 -1.064,-1.064 -0.616,0 -1.078,0.476 -1.078,1.064 0,0.602 0.462,1.078 1.078,1.078 z m 0.756,8.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.44 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58225" />
|
||||
<path
|
||||
d="m 134.556,123.61004 c 0.462,0 1.162,-0.084 1.162,-0.406 v -1.022 c 0,-0.14 -0.098,-0.21 -0.252,-0.196 -0.168,0.014 -0.308,0.014 -0.434,0.014 -0.364,0 -0.602,-0.196 -0.602,-0.602 v -3.318 h 1.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.078 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.078 v -1.61 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.61 h -0.868 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.078 c 0,0.126 0.084,0.21 0.21,0.21 h 0.868 v 3.528 c 0,1.498 0.98,2.002 2.072,2.002 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58227" />
|
||||
<path
|
||||
d="m 140.94328,116.41404 c -1.022,0 -1.68,0.504 -2.072,1.204 v -3.976 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.512 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.59 c 0,0.126 0.084,0.21 0.21,0.21 h 1.512 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.556 c 0,-0.966 0.532,-1.484 1.344,-1.484 0.826,0 1.274,0.518 1.274,1.484 v 3.556 c 0,0.126 0.084,0.21 0.21,0.21 h 1.498 c 0.126,0 0.224,-0.084 0.224,-0.21 v -3.99 c 0,-1.792 -1.05,-2.828 -2.478,-2.828 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58229" />
|
||||
<path
|
||||
d="m 153.96875,113.64204 c -0.126,0 -0.21,0.084 -0.21,0.21 v 6.384 l -3.5,-6.454 c -0.056,-0.098 -0.14,-0.14 -0.252,-0.14 h -1.862 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 h 1.582 c 0.126,0 0.21,-0.084 0.21,-0.21 v -6.384 l 3.5,6.454 c 0.056,0.098 0.14,0.14 0.252,0.14 h 1.862 c 0.126,0 0.21,-0.084 0.21,-0.21 v -9.38 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58231" />
|
||||
<path
|
||||
d="m 159.69847,121.66404 v -7.812 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.582 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 h 4.788 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.358 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58233" />
|
||||
<path
|
||||
d="m 166.21503,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.22 h 1.162 c 2.324,0 3.444,-1.302 3.444,-3.192 0,-1.876 -1.12,-3.178 -3.444,-3.178 h -2.954 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z m 0.21,-8.022 h 1.092 c 0.854,0 1.596,0.294 1.596,1.4 0,1.12 -0.742,1.414 -1.596,1.414 h -1.092 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58235" />
|
||||
<path
|
||||
d="m 178.2045,121.93004 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.554 h 1.554 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.008 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.554 v -1.554 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.008 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.554 h -1.554 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.008 c 0,0.126 0.084,0.21 0.21,0.21 h 1.554 v 1.554 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58237" />
|
||||
<path
|
||||
d="m 186.58612,123.44204 c 0.12601,0 0.21,-0.084 0.21,-0.21 v -3.934 h 2.94 c 0.126,0 0.224,-0.084 0.224,-0.21 v -1.358 c 0,-0.126 -0.098,-0.21 -0.224,-0.21 h -2.94 v -2.1 h 3.122 c 0.126,0 0.224,-0.084 0.224,-0.21 v -1.358 c 0,-0.126 -0.098,-0.21 -0.224,-0.21 h -4.914 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58239" />
|
||||
<path
|
||||
d="m 194.238,123.61004 c 1.022,0 1.764,-0.434 2.24,-1.134 l 0.028,0.756 c 0,0.126 0.084,0.21 0.21,0.21 h 1.358 c 0.126,0 0.224,-0.084 0.224,-0.21 v -6.44 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.372 c -0.126,0 -0.21,0.084 -0.21,0.21 l -0.028,0.756 c -0.462,-0.714 -1.204,-1.134 -2.24,-1.134 -1.932,0 -3.262,1.568 -3.262,3.598 0,2.044 1.33,3.598 3.262,3.598 z m 0.406,-1.764 c -1.022,0 -1.778,-0.728 -1.778,-1.834 0,-1.092 0.756,-1.834 1.778,-1.834 1.022,0 1.736,0.728 1.736,1.834 0,1.106 -0.714,1.834 -1.736,1.834 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58241" />
|
||||
<path
|
||||
d="m 202.30462,123.61004 c 1.372,0 2.562,-0.714 2.576,-2.1 0.014,-1.05 -0.672,-1.638 -1.54,-1.988 l -0.966,-0.378 c -0.378,-0.14 -0.658,-0.336 -0.658,-0.672 0,-0.294 0.21,-0.546 0.658,-0.546 0.406,0 0.798,0.196 1.246,0.574 0.126,0.098 0.224,0.112 0.322,0 l 0.616,-0.742 c 0.07,-0.084 0.098,-0.196 0.014,-0.294 -0.574,-0.686 -1.4,-1.05 -2.296,-1.05 -1.274,0 -2.394,0.756 -2.394,2.072 0,0.952 0.616,1.526 1.498,1.876 l 0.868,0.35 c 0.504,0.21 0.714,0.378 0.714,0.714 0,0.392 -0.308,0.574 -0.77,0.574 -0.532,0 -1.008,-0.238 -1.582,-0.672 -0.112,-0.084 -0.238,-0.112 -0.35,0.056 l -0.49,0.686 c -0.098,0.154 -0.112,0.308 -0.028,0.406 0.546,0.644 1.414,1.134 2.562,1.134 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58243" />
|
||||
<path
|
||||
d="m 208.71225,123.61004 c 0.462,0 1.162,-0.084 1.162,-0.406 v -1.022 c 0,-0.14 -0.098,-0.21 -0.252,-0.196 -0.168,0.014 -0.308,0.014 -0.434,0.014 -0.364,0 -0.602,-0.196 -0.602,-0.602 v -3.318 h 1.078 c 0.126,0 0.21,-0.084 0.21,-0.21 v -1.078 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.078 v -1.61 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.526 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.61 h -0.868 c -0.126,0 -0.21,0.084 -0.21,0.21 v 1.078 c 0,0.126 0.084,0.21 0.21,0.21 h 0.868 v 3.528 c 0,1.498 0.98,2.002 2.072,2.002 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58245" />
|
||||
<path
|
||||
d="m 216.26153,113.81004 c -0.042,-0.112 -0.154,-0.168 -0.266,-0.168 h -1.694 c -0.112,0 -0.224,0.056 -0.266,0.168 l -3.276,9.394 c -0.056,0.14 0.014,0.238 0.168,0.238 h 1.652 c 0.112,0 0.196,-0.042 0.238,-0.168 l 0.476,-1.442 h 3.696 l 0.476,1.442 c 0.042,0.126 0.126,0.168 0.238,0.168 h 1.652 c 0.154,0 0.238,-0.098 0.182,-0.238 z m -1.12,2.352 1.274,3.892 h -2.548 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58247" />
|
||||
<path
|
||||
d="m 222.70722,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -3.22 h 1.162 c 2.324,0 3.444,-1.302 3.444,-3.192 0,-1.876 -1.12,-3.178 -3.444,-3.178 h -2.954 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z m 0.21,-8.022 h 1.092 c 0.854,0 1.596,0.294 1.596,1.4 0,1.12 -0.742,1.414 -1.596,1.414 h -1.092 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58249" />
|
||||
<path
|
||||
d="m 230.82831,123.44204 c 0.126,0 0.21,-0.084 0.21,-0.21 v -9.38 c 0,-0.126 -0.084,-0.21 -0.21,-0.21 h -1.582 c -0.126,0 -0.21,0.084 -0.21,0.21 v 9.38 c 0,0.126 0.084,0.21 0.21,0.21 z"
|
||||
style="font-weight:bold;font-family:'Greycliff CF';-inkscape-font-specification:'Greycliff CF Bold'"
|
||||
id="path58251" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 66 KiB |
@@ -5,8 +5,8 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
|
||||
@@ -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:
|
||||
|
||||
@@ -2,6 +2,102 @@
|
||||
|
||||
## 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
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add support for extensions and updates to the OpenAPI schema in each *path operation*. New docs: [FastAPI Path Operation Advanced Configuration - OpenAPI Extra](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#openapi-extra). Initial PR [#1922](https://github.com/tiangolo/fastapi/pull/1922) by [@edouardlp](https://github.com/edouardlp).
|
||||
* ✨ Add additonal OpenAPI metadata parameters to `FastAPI` class, shown on the automatic API docs UI. New docs: [Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/). Initial PR [#1812](https://github.com/tiangolo/fastapi/pull/1812) by [@dkreeft](https://github.com/dkreeft).
|
||||
* ✨ Add `description` parameter to all the security scheme classes, e.g. `APIKeyQuery(name="key", description="A very cool API key")`. PR [#1757](https://github.com/tiangolo/fastapi/pull/1757) by [@hylkepostma](https://github.com/hylkepostma).
|
||||
* ✨ Update OpenAPI models, supporting recursive models and extensions. PR [#3628](https://github.com/tiangolo/fastapi/pull/3628) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✨ Import and re-export data structures from Starlette, used by Request properties, on `fastapi.datastructures`. Initial PR [#1872](https://github.com/tiangolo/fastapi/pull/1872) by [@jamescurtin](https://github.com/jamescurtin).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Update docs about async and response-model with more gender neutral language. PR [#1869](https://github.com/tiangolo/fastapi/pull/1869) by [@Edward-Knight](https://github.com/Edward-Knight).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Russian translation for `docs/python-types.md`. PR [#3039](https://github.com/tiangolo/fastapi/pull/3039) by [@dukkee](https://github.com/dukkee).
|
||||
* 🌐 Add Chinese translation for `docs/tutorial/dependencies/index.md`. PR [#3489](https://github.com/tiangolo/fastapi/pull/3489) by [@jaystone776](https://github.com/jaystone776).
|
||||
* 🌐 Add Russian translation for `docs/external-links.md`. PR [#3036](https://github.com/tiangolo/fastapi/pull/3036) by [@dukkee](https://github.com/dukkee).
|
||||
* 🌐 Add Chinese translation for `docs/tutorial/dependencies/global-dependencies.md`. PR [#3493](https://github.com/tiangolo/fastapi/pull/3493) by [@jaystone776](https://github.com/jaystone776).
|
||||
* 🌐 Add Portuguese translation for `docs/deployment/versions.md`. PR [#3618](https://github.com/tiangolo/fastapi/pull/3618) by [@lsglucas](https://github.com/lsglucas).
|
||||
* 🌐 Add Japanese translation for `docs/tutorial/security/oauth2-jwt.md`. PR [#3526](https://github.com/tiangolo/fastapi/pull/3526) by [@sattosan](https://github.com/sattosan).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✅ Add the `docs_src` directory to test coverage and update tests. Initial PR [#1904](https://github.com/tiangolo/fastapi/pull/1904) by [@Kludex](https://github.com/Kludex).
|
||||
* 🔧 Add new GitHub templates with forms for new issues. PR [#3612](https://github.com/tiangolo/fastapi/pull/3612) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Add official FastAPI Twitter to docs: [@fastapi](https://twitter.com/fastapi). PR [#3578](https://github.com/tiangolo/fastapi/pull/3578) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.67.0
|
||||
|
||||
|
||||
@@ -2,21 +2,28 @@
|
||||
|
||||
You can customize several metadata configurations in your **FastAPI** application.
|
||||
|
||||
## Title, description, and version
|
||||
## Metadata for API
|
||||
|
||||
You can set the:
|
||||
You can set the following fields that are used in the OpenAPI specification and the automatic API docs UIs:
|
||||
|
||||
* **Title**: used as your API's title/name, in OpenAPI and the automatic API docs UIs.
|
||||
* **Description**: the description of your API, in OpenAPI and the automatic API docs UIs.
|
||||
* **Version**: the version of your API, e.g. `v2` or `2.5.0`.
|
||||
* Useful for example if you had a previous version of the application, also using OpenAPI.
|
||||
| Parameter | Type | Description |
|
||||
|------------|------|-------------|
|
||||
| `title` | `str` | The title of the API. |
|
||||
| `description` | `str` | A short description of the API. It can use Markdown. |
|
||||
| `version` | `string` | The version of the API. This is the version of your own application, not of OpenAPI. For example `2.5.0`. |
|
||||
| `terms_of_service` | `str` | A URL to the Terms of Service for the API. If provided, this has to be a URL. |
|
||||
| `contact` | `dict` | The contact information for the exposed API. It can contain several fields. <details><summary><code>contact</code> fields</summary><table><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>The identifying name of the contact person/organization.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>The URL pointing to the contact information. MUST be in the format of a URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>The email address of the contact person/organization. MUST be in the format of an email address.</td></tr></tbody></table></details> |
|
||||
| `license_info` | `dict` | The license information for the exposed API. It can contain several fields. <details><summary><code>license_info</code> fields</summary><table><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>REQUIRED</strong> (if a <code>license_info</code> is set). The license name used for the API.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>A URL to the license used for the API. MUST be in the format of a URL.</td></tr></tbody></table></details> |
|
||||
|
||||
To set them, use the parameters `title`, `description`, and `version`:
|
||||
You can set them as follows:
|
||||
|
||||
```Python hl_lines="4-6"
|
||||
```Python hl_lines="3-16 19-31"
|
||||
{!../../../docs_src/metadata/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can write Markdown in the `description` field and it will be rendered in the output.
|
||||
|
||||
With this configuration, the automatic API docs would look like:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
@@ -47,7 +47,7 @@ And we are using this model to declare our input and the same model to declare o
|
||||
|
||||
Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
|
||||
|
||||
In this case, it might not be a problem, because the user himself is sending the password.
|
||||
In this case, it might not be a problem, because the user themself is sending the password.
|
||||
|
||||
But if we use the same model for another *path operation*, we could be sending our user's passwords to every client.
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -180,7 +186,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
{% block announce %}
|
||||
<div class="announce-wrapper">
|
||||
<div id="announce-left">
|
||||
<div class="item">
|
||||
<a class="announce-link" href="https://twitter.com/fastapi" target="_blank">
|
||||
<span class="twemoji twitter">
|
||||
{% include ".icons/fontawesome/brands/twitter.svg" %}
|
||||
</span> Follow <strong>@fastapi</strong> on <strong>Twitter</strong> to stay updated
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a class="announce-link" href="https://fastapi.tiangolo.com/newsletter/">
|
||||
<span class="twemoji">
|
||||
@@ -15,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
@@ -86,7 +87,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
@@ -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.
|
||||
182
docs/fr/docs/deployment/docker.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Déployer avec Docker
|
||||
|
||||
Dans cette section, vous verrez des instructions et des liens vers des guides pour savoir comment :
|
||||
|
||||
* Faire de votre application **FastAPI** une image/conteneur Docker avec une performance maximale. En environ **5 min**.
|
||||
* (Optionnellement) comprendre ce que vous, en tant que développeur, devez savoir sur HTTPS.
|
||||
* Configurer un cluster en mode Docker Swarm avec HTTPS automatique, même sur un simple serveur à 5 dollars US/mois. En environ **20 min**.
|
||||
* Générer et déployer une application **FastAPI** complète, en utilisant votre cluster Docker Swarm, avec HTTPS, etc. En environ **10 min**.
|
||||
|
||||
Vous pouvez utiliser <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> pour le déploiement. Il présente plusieurs avantages comme la sécurité, la réplicabilité, la simplicité de développement, etc.
|
||||
|
||||
Si vous utilisez Docker, vous pouvez utiliser l'image Docker officielle :
|
||||
|
||||
## <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
|
||||
|
||||
Cette image est dotée d'un mécanisme d'"auto-tuning", de sorte qu'il vous suffit d'ajouter votre code pour obtenir automatiquement des performances très élevées. Et sans faire de sacrifices.
|
||||
|
||||
Mais vous pouvez toujours changer et mettre à jour toutes les configurations avec des variables d'environnement ou des fichiers de configuration.
|
||||
|
||||
!!! tip "Astuce"
|
||||
Pour voir toutes les configurations et options, rendez-vous sur la page de l'image Docker : <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
## Créer un `Dockerfile`
|
||||
|
||||
* Allez dans le répertoire de votre projet.
|
||||
* Créez un `Dockerfile` avec :
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
|
||||
COPY ./app /app
|
||||
```
|
||||
|
||||
### Applications plus larges
|
||||
|
||||
Si vous avez suivi la section sur la création d' [Applications avec plusieurs fichiers](../tutorial/bigger-applications.md){.internal-link target=_blank}, votre `Dockerfile` pourrait ressembler à ceci :
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
|
||||
COPY ./app /app/app
|
||||
```
|
||||
|
||||
### Raspberry Pi et autres architectures
|
||||
|
||||
Si vous utilisez Docker sur un Raspberry Pi (qui a un processeur ARM) ou toute autre architecture, vous pouvez créer un `Dockerfile` à partir de zéro, basé sur une image de base Python (qui est multi-architecture) et utiliser Uvicorn seul.
|
||||
|
||||
Dans ce cas, votre `Dockerfile` pourrait ressembler à ceci :
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.7
|
||||
|
||||
RUN pip install fastapi uvicorn
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
COPY ./app /app
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
## Créer le code **FastAPI**.
|
||||
|
||||
* Créer un répertoire `app` et y entrer.
|
||||
* Créez un fichier `main.py` avec :
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
* Vous devriez maintenant avoir une structure de répertoire telle que :
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ └── main.py
|
||||
└── Dockerfile
|
||||
```
|
||||
|
||||
## Construire l'image Docker
|
||||
|
||||
* Allez dans le répertoire du projet (dans lequel se trouve votre `Dockerfile`, contenant votre répertoire `app`).
|
||||
* Construisez votre image FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ docker build -t myimage .
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Démarrer le conteneur Docker
|
||||
|
||||
* Exécutez un conteneur basé sur votre image :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ docker run -d --name mycontainer -p 80:80 myimage
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Vous disposez maintenant d'un serveur FastAPI optimisé dans un conteneur Docker. Configuré automatiquement pour votre
|
||||
serveur actuel (et le nombre de cœurs du CPU).
|
||||
|
||||
## Vérifier
|
||||
|
||||
Vous devriez pouvoir accéder à votre application via l'URL de votre conteneur Docker, par exemple : <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez quelque chose comme :
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
## Documentation interactive de l'API
|
||||
|
||||
Vous pouvez maintenant visiter <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez la documentation interactive automatique de l'API (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
|
||||
|
||||

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

|
||||
|
||||
## Traefik
|
||||
|
||||
<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a> est un reverse proxy/load balancer
|
||||
haute performance. Il peut faire office de "Proxy de terminaison TLS" (entre autres fonctionnalités).
|
||||
|
||||
Il est intégré à Let's Encrypt. Ainsi, il peut gérer toutes les parties HTTPS, y compris l'acquisition et le renouvellement des certificats.
|
||||
|
||||
Il est également intégré à Docker. Ainsi, vous pouvez déclarer vos domaines dans les configurations de chaque application et faire en sorte qu'elles lisent ces configurations, génèrent les certificats HTTPS et servent via HTTPS à votre application automatiquement, sans nécessiter aucune modification de leurs configurations.
|
||||
|
||||
---
|
||||
|
||||
Avec ces informations et ces outils, passez à la section suivante pour tout combiner.
|
||||
|
||||
## Cluster en mode Docker Swarm avec Traefik et HTTPS
|
||||
|
||||
Vous pouvez avoir un cluster en mode Docker Swarm configuré en quelques minutes (environ 20 min) avec un processus Traefik principal gérant HTTPS (y compris l'acquisition et le renouvellement des certificats).
|
||||
|
||||
En utilisant le mode Docker Swarm, vous pouvez commencer par un "cluster" d'une seule machine (il peut même s'agir
|
||||
d'un serveur à 5 USD/mois) et ensuite vous pouvez vous développer autant que vous le souhaitez en ajoutant d'autres serveurs.
|
||||
|
||||
Pour configurer un cluster en mode Docker Swarm avec Traefik et la gestion de HTTPS, suivez ce guide :
|
||||
|
||||
### <a href="https://medium.com/@tiangolo/docker-swarm-mode-and-traefik-for-a-https-cluster-20328dba6232" class="external-link" target="_blank">Docker Swarm Mode et Traefik pour un cluster HTTPS</a>
|
||||
|
||||
### Déployer une application FastAPI
|
||||
|
||||
La façon la plus simple de tout mettre en place, serait d'utiliser les [**Générateurs de projet FastAPI**](../project-generation.md){.internal-link target=_blank}.
|
||||
|
||||
Le génerateur de projet adéquat est conçu pour être intégré à ce cluster Docker Swarm avec Traefik et HTTPS décrit ci-dessus.
|
||||
|
||||
Vous pouvez générer un projet en 2 min environ.
|
||||
|
||||
Le projet généré a des instructions pour le déployer et le faire prend 2 min de plus.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
165
docs/fr/docs/tutorial/body.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Corps de la requête
|
||||
|
||||
Quand vous avez besoin d'envoyer de la donnée depuis un client (comme un navigateur) vers votre API, vous l'envoyez en tant que **corps de requête**.
|
||||
|
||||
Le corps d'une **requête** est de la donnée envoyée par le client à votre API. Le corps d'une **réponse** est la donnée envoyée par votre API au client.
|
||||
|
||||
Votre API aura presque toujours à envoyer un corps de **réponse**. Mais un client n'a pas toujours à envoyer un corps de **requête**.
|
||||
|
||||
Pour déclarer un corps de **requête**, on utilise les modèles de <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> en profitant de tous leurs avantages et fonctionnalités.
|
||||
|
||||
!!! info
|
||||
Pour envoyer de la donnée, vous devriez utiliser : `POST` (le plus populaire), `PUT`, `DELETE` ou `PATCH`.
|
||||
|
||||
Envoyer un corps dans une requête `GET` a un comportement non défini dans les spécifications, cela est néanmoins supporté par **FastAPI**, seulement pour des cas d'utilisation très complexes/extrêmes.
|
||||
|
||||
Ceci étant découragé, la documentation interactive générée par Swagger UI ne montrera pas de documentation pour le corps d'une requête `GET`, et les proxys intermédiaires risquent de ne pas le supporter.
|
||||
|
||||
## Importez le `BaseModel` de Pydantic
|
||||
|
||||
Commencez par importer la classe `BaseModel` du module `pydantic` :
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Créez votre modèle de données
|
||||
|
||||
Déclarez ensuite votre modèle de données en tant que classe qui hérite de `BaseModel`.
|
||||
|
||||
Utilisez les types Python standard pour tous les attributs :
|
||||
|
||||
```Python hl_lines="7-11"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
Tout comme pour la déclaration de paramètres de requête, quand un attribut de modèle a une valeur par défaut, il n'est pas nécessaire. Sinon, cet attribut doit être renseigné dans le corps de la requête. Pour rendre ce champ optionnel simplement, utilisez `None` comme valeur par défaut.
|
||||
|
||||
Par exemple, le modèle ci-dessus déclare un "objet" JSON (ou `dict` Python) tel que :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "An optional description",
|
||||
"price": 45.2,
|
||||
"tax": 3.5
|
||||
}
|
||||
```
|
||||
|
||||
...`description` et `tax` étant des attributs optionnels (avec `None` comme valeur par défaut), cet "objet" JSON serait aussi valide :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 45.2
|
||||
}
|
||||
```
|
||||
|
||||
## Déclarez-le comme paramètre
|
||||
|
||||
Pour l'ajouter à votre *opération de chemin*, déclarez-le comme vous déclareriez des paramètres de chemin ou de requête :
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
...et déclarez que son type est le modèle que vous avez créé : `Item`.
|
||||
|
||||
## Résultats
|
||||
|
||||
En utilisant uniquement les déclarations de type Python, **FastAPI** réussit à :
|
||||
|
||||
* Lire le contenu de la requête en tant que JSON.
|
||||
* Convertir les types correspondants (si nécessaire).
|
||||
* Valider la donnée.
|
||||
* Si la donnée est invalide, une erreur propre et claire sera renvoyée, indiquant exactement où était la donnée incorrecte.
|
||||
* Passer la donnée reçue dans le paramètre `item`.
|
||||
* Ce paramètre ayant été déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support offert par l'éditeur (auto-complétion, etc.) pour tous les attributs de ce paramètre et les types de ces attributs.
|
||||
* Générer des définitions <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> pour votre modèle, qui peuvent être utilisées où vous en avez besoin dans votre projet ensuite.
|
||||
* Ces schémas participeront à la constitution du schéma généré OpenAPI, et seront donc utilisés par les documentations automatiquement générées.
|
||||
|
||||
## Documentation automatique
|
||||
|
||||
Les schémas JSON de vos modèles seront intégrés au schéma OpenAPI global de votre application, et seront donc affichés dans la documentation interactive de l'API :
|
||||
|
||||
<img src="/img/tutorial/body/image01.png">
|
||||
|
||||
Et seront aussi utilisés dans chaque *opération de chemin* de la documentation utilisant ces modèles :
|
||||
|
||||
<img src="/img/tutorial/body/image02.png">
|
||||
|
||||
## Support de l'éditeur
|
||||
|
||||
Dans votre éditeur, vous aurez des annotations de types et de l'auto-complétion partout dans votre fonction (ce qui n'aurait pas été le cas si vous aviez utilisé un classique `dict` plutôt qu'un modèle Pydantic) :
|
||||
|
||||
<img src="/img/tutorial/body/image03.png">
|
||||
|
||||
Et vous obtenez aussi de la vérification d'erreur pour les opérations incorrectes de types :
|
||||
|
||||
<img src="/img/tutorial/body/image04.png">
|
||||
|
||||
Ce n'est pas un hasard, ce framework entier a été bati avec ce design comme objectif.
|
||||
|
||||
Et cela a été rigoureusement testé durant la phase de design, avant toute implémentation, pour s'assurer que cela fonctionnerait avec tous les éditeurs.
|
||||
|
||||
Des changements sur Pydantic ont même été faits pour supporter cela.
|
||||
|
||||
Les captures d'écrans précédentes ont été prises sur <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
|
||||
|
||||
Mais vous auriez le même support de l'éditeur avec <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> et la majorité des autres éditeurs de code Python.
|
||||
|
||||
<img src="/img/tutorial/body/image05.png">
|
||||
|
||||
!!! tip "Astuce"
|
||||
Si vous utilisez <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> comme éditeur, vous pouvez utiliser le Plugin <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>.
|
||||
|
||||
Ce qui améliore le support pour les modèles Pydantic avec :
|
||||
|
||||
* de l'auto-complétion
|
||||
* des vérifications de type
|
||||
* du "refactoring" (ou remaniement de code)
|
||||
* de la recherche
|
||||
* de l'inspection
|
||||
|
||||
## Utilisez le modèle
|
||||
|
||||
Dans la fonction, vous pouvez accéder à tous les attributs de l'objet du modèle directement :
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Corps de la requête + paramètres de chemin
|
||||
|
||||
Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la même *opération de chemin*.
|
||||
|
||||
**FastAPI** est capable de reconnaître que les paramètres de la fonction qui correspondent aux paramètres de chemin doivent être **récupérés depuis le chemin**, et que les paramètres de fonctions déclarés comme modèles Pydantic devraient être **récupérés depuis le corps de la requête**.
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Corps de la requête + paramètres de chemin et de requête
|
||||
|
||||
Vous pouvez aussi déclarer un **corps**, et des paramètres de **chemin** et de **requête** dans la même *opération de chemin*.
|
||||
|
||||
**FastAPI** saura reconnaître chacun d'entre eux et récupérer la bonne donnée au bon endroit.
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
Les paramètres de la fonction seront reconnus comme tel :
|
||||
|
||||
* Si le paramètre est aussi déclaré dans le **chemin**, il sera utilisé comme paramètre de chemin.
|
||||
* Si le paramètre est d'un **type singulier** (comme `int`, `float`, `str`, `bool`, etc.), il sera interprété comme un paramètre de **requête**.
|
||||
* Si le paramètre est déclaré comme ayant pour type un **modèle Pydantic**, il sera interprété comme faisant partie du **corps** de la requête.
|
||||
|
||||
!!! note
|
||||
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `=None`.
|
||||
|
||||
Le type `Optional` dans `Optional[str]` n'est pas utilisé par **FastAPI**, mais sera utile à votre éditeur pour améliorer le support offert par ce dernier et détecter plus facilement des erreurs de type.
|
||||
|
||||
## Sans Pydantic
|
||||
|
||||
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres de **Corps**. Pour cela, allez voir la partie de la documentation sur [Corps de la requête - Paramètres multiples](body-multiple-params.md){.internal-link target=_blank}.
|
||||
@@ -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:
|
||||
@@ -84,7 +89,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
@@ -76,7 +77,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
0
docs/id/overrides/.gitignore
vendored
Normal file
@@ -1,13 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block announce %}
|
||||
<a class="announce" href="https://fastapi.tiangolo.com/newsletter/">
|
||||
<span class="twemoji">
|
||||
{% include ".icons/material/email.svg" %}
|
||||
</span> Subscribe to the <strong>FastAPI and friends</strong> newsletter 🎉
|
||||
</a>
|
||||
<!-- <a class="announce" href="https://tripetto.app/run/RXZ6OLDBXX?s=dc" target="_blank">
|
||||
<span class="twemoji">
|
||||
</span>Fill the first-ever <strong>FastAPI user survey</strong> for a chance to win official <strong>FastAPI and Typer stickers</strong> 🎁
|
||||
</a> -->
|
||||
{% endblock %}
|
||||
@@ -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:
|
||||
@@ -76,7 +77,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
## データについて - 技術詳細
|
||||
|
||||
@@ -31,7 +31,6 @@ FastAPIの機能はすべて、標準のPython 3.6型宣言に基づいていま
|
||||
型を使用した標準的なPythonを記述します:
|
||||
|
||||
```Python
|
||||
from typing import List, Dict
|
||||
from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
266
docs/ja/docs/tutorial/security/oauth2-jwt.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# パスワード(およびハッシュ化)によるOAuth2、JWTトークンによるBearer
|
||||
|
||||
これでセキュリティの流れが全てわかったので、<abbr title="JSON Web Tokens">JWT</abbr>トークンと安全なパスワードのハッシュ化を使用して、実際にアプリケーションを安全にしてみましょう。
|
||||
|
||||
このコードは、アプリケーションで実際に使用したり、パスワードハッシュをデータベースに保存するといった用途に利用できます。
|
||||
|
||||
本章では、前章の続きから始めて、コードをアップデートしていきます。
|
||||
|
||||
## JWT について
|
||||
|
||||
JWTとは「JSON Web Tokens」の略称です。
|
||||
|
||||
JSONオブジェクトをスペースのない長く密集した文字列で表現したトークンの仕様です。例えば次のようになります:
|
||||
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
||||
```
|
||||
|
||||
これらは暗号化されていないので、誰でもコンテンツから情報を復元できてしまいます。
|
||||
|
||||
しかし、トークンは署名されているため、あなたが発行したトークンを受け取った人は、あなたが実際に発行したということを検証できます。
|
||||
|
||||
例えば、1週間の有効期限を持つトークンを作成したとします。ユーザーが翌日そのトークンを持って戻ってきたとき、そのユーザーはまだシステムにログインしていることがわかります。
|
||||
|
||||
1週間後、トークンが期限切れとなるとどうなるでしょうか?ユーザーは認可されず、新しいトークンを得るために再びサインインしなければなりません。また、ユーザー(または第三者)がトークンを修正して有効期限を変更しようとした場合、署名が一致しないため、トークンの修正を検知できます。
|
||||
|
||||
JWT トークンを使って遊んでみたいという方は、<a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a> をチェックしてください。
|
||||
|
||||
## `python-jose` のインストール
|
||||
|
||||
PythonでJWTトークンの生成と検証を行うために、`python-jose`をインストールする必要があります:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install python-jose[cryptography]
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
また、<a href="https://github.com/mpdavis/python-jose" class="external-link" target="_blank">Python-jose</a>だけではなく、暗号を扱うためのパッケージを追加で必要とします。
|
||||
|
||||
ここでは、推奨されているものを使用します:<a href="https://cryptography.io/" class="external-link" target="_blank">pyca/cryptography</a>。
|
||||
|
||||
!!! tip "豆知識"
|
||||
このチュートリアルでは以前、<a href="https://pyjwt.readthedocs.io/" class="external-link" target="_blank">PyJWT</a>を使用していました。
|
||||
|
||||
しかし、Python-joseは、PyJWTのすべての機能に加えて、後に他のツールと統合して構築する際におそらく必要となる可能性のあるいくつかの追加機能を提供しています。そのため、代わりにPython-joseを使用するように更新されました。
|
||||
|
||||
## パスワードのハッシュ化
|
||||
|
||||
「ハッシュ化」とは、あるコンテンツ(ここではパスワード)を、規則性のないバイト列(単なる文字列)に変換することです。
|
||||
|
||||
特徴として、全く同じ内容(全く同じパスワード)を渡すと、全く同じ規則性のないバイト列に変換されます。
|
||||
|
||||
しかし、規則性のないバイト列から元のパスワードに戻すことはできません。
|
||||
|
||||
### パスワードのハッシュ化を使う理由
|
||||
|
||||
データベースが盗まれても、ユーザーの平文のパスワードは盗まれず、ハッシュ値だけが盗まれます。
|
||||
|
||||
したがって、泥棒はそのパスワードを別のシステムで使えません(多くのユーザーはどこでも同じパスワードを使用しているため、危険性があります)。
|
||||
|
||||
## `passlib` のインストール
|
||||
|
||||
PassLib は、パスワードのハッシュを処理するための優れたPythonパッケージです。
|
||||
|
||||
このパッケージは、多くの安全なハッシュアルゴリズムとユーティリティをサポートします。
|
||||
|
||||
推奨されるアルゴリズムは「Bcrypt」です。
|
||||
|
||||
そのため、Bcryptを指定してPassLibをインストールします:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install passlib[bcrypt]
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
!!! tip "豆知識"
|
||||
`passlib`を使用すると、**Django**や**Flask**のセキュリティプラグインなどで作成されたパスワードを読み取れるように設定できます。
|
||||
|
||||
例えば、Djangoアプリケーションからデータベース内の同じデータをFastAPIアプリケーションと共有できるだけではなく、同じデータベースを使用してDjangoアプリケーションを徐々に移行することもできます。
|
||||
|
||||
また、ユーザーはDjangoアプリまたは**FastAPI**アプリからも、同時にログインできるようになります。
|
||||
|
||||
|
||||
## パスワードのハッシュ化と検証
|
||||
|
||||
必要なツールを `passlib`からインポートします。
|
||||
|
||||
PassLib の「context」を作成します。これは、パスワードのハッシュ化と検証に使用されるものです。
|
||||
|
||||
!!! tip "豆知識"
|
||||
PassLibのcontextには、検証だけが許された非推奨の古いハッシュアルゴリズムを含む、様々なハッシュアルゴリズムを使用した検証機能もあります。
|
||||
|
||||
例えば、この機能を使用して、別のシステム(Djangoなど)によって生成されたパスワードを読み取って検証し、Bcryptなどの別のアルゴリズムを使用して新しいパスワードをハッシュするといったことができます。
|
||||
|
||||
そして、同時にそれらはすべてに互換性があります。
|
||||
|
||||
ユーザーから送られてきたパスワードをハッシュ化するユーティリティー関数を作成します。
|
||||
|
||||
また、受け取ったパスワードが保存されているハッシュと一致するかどうかを検証するユーティリティも作成します。
|
||||
|
||||
さらに、ユーザーを認証して返す関数も作成します。
|
||||
|
||||
```Python hl_lines="7 48 55-56 59-60 69-75"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
!!! note "備考"
|
||||
新しい(偽の)データベース`fake_users_db`を確認すると、ハッシュ化されたパスワードが次のようになっていることがわかります:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`
|
||||
|
||||
## JWTトークンの取り扱い
|
||||
|
||||
インストールした複数のモジュールをインポートします。
|
||||
|
||||
JWTトークンの署名に使用されるランダムな秘密鍵を生成します。
|
||||
|
||||
安全なランダム秘密鍵を生成するには、次のコマンドを使用します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ openssl rand -hex 32
|
||||
|
||||
09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
そして、出力された文字列を変数`SECRET_KEY`にコピーします。(例に記載している秘密鍵は実際に使用しないでください)
|
||||
|
||||
JWTトークンの署名に使用するアルゴリズム`"HS256"`を指定した変数`ALGORITHM`を作成します。
|
||||
|
||||
トークンの有効期限を指定した変数`ACCESS_TOKEN_EXPIRE_MINUTES`を作成します。
|
||||
|
||||
レスポンスのトークンエンドポイントで使用するPydanticモデルを定義します。
|
||||
|
||||
新しいアクセストークンを生成するユーティリティ関数を作成します。
|
||||
|
||||
```Python hl_lines="6 12-14 28-30 78-86"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
## 依存関係の更新
|
||||
|
||||
`get_current_user`を更新して、先ほどと同じトークンを受け取るようにしますが、今回はJWTトークンを使用します。
|
||||
|
||||
受け取ったトークンを復号して検証し、現在のユーザーを返します。
|
||||
|
||||
トークンが無効な場合は、すぐにHTTPエラーを返します。
|
||||
|
||||
```Python hl_lines="89-106"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
## `/token` パスオペレーションの更新
|
||||
|
||||
トークンの有効期限を表す`timedelta`を作成します。
|
||||
|
||||
JWTアクセストークンを作成し、それを返します。
|
||||
|
||||
```Python hl_lines="115-128"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
### JWTの"subject" `sub` についての技術的な詳細
|
||||
|
||||
JWTの仕様では、トークンのsubjectを表すキー`sub`があるとされています。
|
||||
|
||||
使用するかどうかは任意ですが、`sub`はユーザーの識別情報を入れるように規定されているので、ここで使用します。
|
||||
|
||||
JWTは、ユーザーを識別して、そのユーザーがAPI上で直接操作を実行できるようにする以外にも、他の用途で使用されることがあります。
|
||||
|
||||
例えば、「車」や「ブログ記事」を識別することができます。
|
||||
|
||||
そして、「ドライブ」(車の場合)や「編集」(ブログの場合)など、そのエンティティに関する権限も追加できます。
|
||||
|
||||
また、JWTトークンをユーザー(またはボット)に渡すことができます。ユーザーは、JWTトークンを使用するだけで、アカウントを持っていなくても、APIが生成したJWTトークンを使ってそれらの行動(車の運転、ブログ投稿の編集)を実行できるのです。
|
||||
|
||||
これらのアイデアを使用すると、JWTをより高度なシナリオに使用できます。
|
||||
|
||||
しかしながら、それらのエンティティのいくつかが同じIDを持つ可能性があります。例えば、`foo`(ユーザー`foo`、車 `foo`、ブログ投稿`foo`)などです。
|
||||
|
||||
IDの衝突を回避するために、ユーザーのJWTトークンを作成するとき、subキーの値にプレフィックスを付けることができます(例えば、`username:`)。したがって、この例では、`sub`の値は次のようになっている可能性があります:`username:johndoe`
|
||||
|
||||
覚えておくべき重要なことは、`sub`キーはアプリケーション全体で一意の識別子を持ち、文字列である必要があるということです。
|
||||
|
||||
## 確認
|
||||
|
||||
サーバーを実行し、ドキュメントに移動します:<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>
|
||||
|
||||
次のようなユーザーインターフェイスが表示されます:
|
||||
|
||||
<img src="/img/tutorial/security/image07.png">
|
||||
|
||||
前回と同じ方法でアプリケーションの認可を行います。
|
||||
|
||||
次の認証情報を使用します:
|
||||
|
||||
Username: `johndoe`
|
||||
Password: `secret`
|
||||
|
||||
!!! check "確認"
|
||||
コードのどこにも平文のパスワード"`secret`"はなく、ハッシュ化されたものしかないことを確認してください。
|
||||
|
||||
<img src="/img/tutorial/security/image08.png">
|
||||
|
||||
エンドポイント`/users/me/`を呼び出すと、次のようなレスポンスが得られます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"username": "johndoe",
|
||||
"email": "johndoe@example.com",
|
||||
"full_name": "John Doe",
|
||||
"disabled": false
|
||||
}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/security/image09.png">
|
||||
|
||||
開発者ツールを開くと、送信されるデータにはトークンだけが含まれており、パスワードはユーザーを認証してアクセストークンを取得する最初のリクエストでのみ送信され、その後は送信されないことがわかります。
|
||||
|
||||
<img src="/img/tutorial/security/image10.png">
|
||||
|
||||
!!! note "備考"
|
||||
ヘッダーの`Authorization`には、`Bearer`で始まる値があります。
|
||||
|
||||
## `scopes` を使った高度なユースケース
|
||||
|
||||
OAuth2には、「スコープ」という概念があります。
|
||||
|
||||
これらを利用して、JWTトークンに特定の権限セットを追加することができます。
|
||||
|
||||
そして、このトークンをユーザーに直接、または第三者に与えて、制限付きでAPIを操作できます。
|
||||
|
||||
これらの使用方法や**FastAPI**への統合方法については、**高度なユーザーガイド**で後ほど説明します。
|
||||
|
||||
## まとめ
|
||||
|
||||
ここまでの説明で、OAuth2やJWTなどの規格を使った安全な**FastAPI**アプリケーションを設定することができます。
|
||||
|
||||
ほとんどのフレームワークにおいて、セキュリティを扱うことは非常に複雑な課題となります。
|
||||
|
||||
簡略化しすぎたパッケージの多くは、データモデルやデータベース、利用可能な機能について多くの妥協をしなければなりません。そして、あまりにも単純化されたパッケージの中には、実はセキュリティ上の欠陥があるものもあります。
|
||||
|
||||
---
|
||||
|
||||
**FastAPI**は、どのようなデータベース、データモデル、ツールに対しても妥協することはありません。
|
||||
|
||||
そのため、プロジェクトに合わせて自由に選択することができます。
|
||||
|
||||
また、**FastAPI**は外部パッケージを統合するために複雑な仕組みを必要としないため、`passlib`や`python-jose`のようなよく整備され広く使われている多くのパッケージを直接使用することができます。
|
||||
|
||||
しかし、柔軟性、堅牢性、セキュリティを損なうことなく、可能な限りプロセスを簡素化するためのツールを提供します。
|
||||
|
||||
また、OAuth2のような安全で標準的なプロトコルを比較的簡単な方法で使用できるだけではなく、実装することもできます。
|
||||
|
||||
OAuth2の「スコープ」を使って、同じ基準でより細かい権限システムを実現する方法については、**高度なユーザーガイド**で詳しく説明しています。スコープ付きのOAuth2は、Facebook、Google、GitHub、Microsoft、Twitterなど、多くの大手認証プロバイダが、サードパーティのアプリケーションと自社のAPIとのやり取りをユーザーに代わって認可するために使用している仕組みです。
|
||||
@@ -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,6 +69,7 @@ nav:
|
||||
- tutorial/body-updates.md
|
||||
- セキュリティ:
|
||||
- tutorial/security/first-steps.md
|
||||
- tutorial/security/oauth2-jwt.md
|
||||
- tutorial/middleware.md
|
||||
- tutorial/cors.md
|
||||
- tutorial/static-files.md
|
||||
@@ -97,7 +99,7 @@ markdown_extensions:
|
||||
permalink: true
|
||||
- markdown.extensions.codehilite:
|
||||
guess_lang: false
|
||||
- markdown_include.include:
|
||||
- mdx_include:
|
||||
base_path: docs
|
||||
- admonition
|
||||
- codehilite
|
||||
@@ -106,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:
|
||||
@@ -115,7 +117,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -159,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
|
||||
|
||||
@@ -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:
|
||||
@@ -82,7 +83,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
@@ -76,7 +77,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
24
docs/pt/docs/advanced/index.md
Normal 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
@@ -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>.
|
||||
48
docs/pt/docs/deployment/https.md
Normal 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.
|
||||
87
docs/pt/docs/deployment/versions.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Sobre as versões do FastAPI
|
||||
|
||||
**FastAPI** já está sendo usado em produção em diversas aplicações e sistemas, a cobertura de testes é mantida em 100%, mas seu desenvolvimento está avançando rapidamente.
|
||||
|
||||
Novos recursos são adicionados com frequência, bugs são corrigidos regularmente e o código está sempre melhorando.
|
||||
|
||||
Esse é o motivo das versões atuais estarem em `0.x.x`, significando que em cada versão pode haver mudanças significativas, tudo isso seguindo as <a href="https://semver.org/lang/pt-BR/" class="external-link" target="_blank">convenções de controle de versão semântica.</a>
|
||||
|
||||
Já é possível criar aplicativos de produção com **FastAPI** (e provavelmente você já faz isso há algum tempo), apenas precisando ter certeza de usar uma versão que funcione corretamente com o resto do seu código.
|
||||
|
||||
## Fixe a sua versão de `fastapi`
|
||||
|
||||
A primeira coisa que você deve fazer é "fixar" a versão do **FastAPI** que você está utilizando na mais atual, na qual você sabe que funciona corretamente para o seu aplicativo.
|
||||
|
||||
Por exemplo, supondo que você está usando a versão `0.45.0` em sua aplicação.
|
||||
|
||||
Caso você utilize o arquivo `requirements.txt`, você poderia especificar a versão com:
|
||||
|
||||
```txt
|
||||
fastapi==0.45.0
|
||||
```
|
||||
|
||||
Isso significa que você conseguiria utilizar a versão exata `0.45.0`.
|
||||
|
||||
Ou, você poderia fixá-la com:
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
isso significa que você iria usar as versões `0.45.0` ou acima, mas inferiores à `0.46.0`, por exemplo, a versão `0.45.2` ainda seria aceita.
|
||||
|
||||
Se você usar qualquer outra ferramenta para gerenciar suas instalações, como Poetry, Pipenv ou outras, todas elas têm uma maneira que você pode usar para definir as versões específicas dos seus pacotes.
|
||||
|
||||
## Versões disponíveis
|
||||
|
||||
Você pode ver as versões disponíveis (por exemplo, para verificar qual é a versão atual) em [Release Notes](../release-notes.md){.internal-link target=\_blank}.
|
||||
|
||||
## Sobre versões
|
||||
|
||||
Seguindo as convenções de controle de versão semântica, qualquer versão abaixo de `1.0.0` pode adicionar mudanças significativas.
|
||||
|
||||
FastAPI também segue a convenção de que qualquer alteração de versão "PATCH" é para correção de bugs e alterações não significativas.
|
||||
|
||||
!!! tip "Dica"
|
||||
O "PATCH" é o último número, por exemplo, em `0.2.3`, a versão PATCH é `3`.
|
||||
|
||||
Logo, você deveria conseguir fixar a versão, como:
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
Mudanças significativas e novos recursos são adicionados em versões "MINOR".
|
||||
|
||||
!!! tip "Dica"
|
||||
O "MINOR" é o número que está no meio, por exemplo, em `0.2.3`, a versão MINOR é `2`.
|
||||
|
||||
## Atualizando as versões do FastAPI
|
||||
|
||||
Você deve adicionar testes para a sua aplicação.
|
||||
|
||||
Com **FastAPI** isso é muito fácil (graças a Starlette), verifique a documentação: [Testing](../tutorial/testing.md){.internal-link target=\_blank}
|
||||
|
||||
Após a criação dos testes, você pode atualizar a sua versão do **FastAPI** para uma mais recente, execute os testes para se certificar de que todo o seu código está funcionando corretamente.
|
||||
|
||||
Se tudo estiver funcionando, ou após você realizar as alterações necessárias e todos os testes estiverem passando, então você pode fixar sua versão de `FastAPI` para essa mais nova.
|
||||
|
||||
## Sobre Starlette
|
||||
|
||||
Não é recomendado fixar a versão de `starlette`.
|
||||
|
||||
Versões diferentes de **FastAPI** utilizarão uma versão específica e mais recente de Starlette.
|
||||
|
||||
Então, você pode deixar **FastAPI** escolher a versão compatível e correta de Starlette.
|
||||
|
||||
## Sobre Pydantic
|
||||
|
||||
Pydantic incluí os testes para **FastAPI** em seus próprios testes, então as novas versões de Pydantic (acima da `1.0.0`) sempre serão compatíveis com FastAPI.
|
||||
|
||||
Você pode fixar qualquer versão de Pydantic que desejar, desde que seja acima da `1.0.0` e abaixo da `2.0.0`.
|
||||
|
||||
Por exemplo:
|
||||
|
||||
```txt
|
||||
pydantic>=1.2.0,<2.0.0
|
||||
```
|
||||
178
docs/pt/docs/fastapi-people.md
Normal 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 🤷).
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
246
docs/pt/docs/tutorial/path-params.md
Normal 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).
|
||||
@@ -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,14 +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
|
||||
@@ -71,7 +78,7 @@ markdown_extensions:
|
||||
permalink: true
|
||||
- markdown.extensions.codehilite:
|
||||
guess_lang: false
|
||||
- markdown_include.include:
|
||||
- mdx_include:
|
||||
base_path: docs
|
||||
- admonition
|
||||
- codehilite
|
||||
@@ -80,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:
|
||||
@@ -89,7 +96,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -133,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
|
||||
|
||||
82
docs/ru/docs/external-links.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Внешние ссылки и статьи
|
||||
|
||||
**FastAPI** имеет отличное и постоянно растущее сообщество.
|
||||
|
||||
Существует множество сообщений, статей, инструментов и проектов, связанных с **FastAPI**.
|
||||
|
||||
Вот неполный список некоторых из них.
|
||||
|
||||
!!! tip
|
||||
Если у вас есть статья, проект, инструмент или что-либо, связанное с **FastAPI**, что еще не перечислено здесь, создайте <a href="https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">Pull Request</a>.
|
||||
|
||||
## Статьи
|
||||
|
||||
### На английском
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.english %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### На японском
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.japanese %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### На вьетнамском
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.vietnamese %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### На русском
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.russian %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### На немецком
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.german %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Подкасты
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.podcasts.english %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Talks
|
||||
|
||||
{% if external_links %}
|
||||
{% for article in external_links.talks.english %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Проекты
|
||||
|
||||
Последние GitHub-проекты с пометкой `fastapi`:
|
||||
|
||||
<div class="github-topic-projects">
|
||||
</div>
|
||||
314
docs/ru/docs/python-types.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# Введение в аннотации типов Python
|
||||
|
||||
Python имеет поддержку необязательных аннотаций типов.
|
||||
|
||||
**Аннотации типов** являются специальным синтаксисом, который позволяет определять <abbr title="например: str, int, float, bool">тип</abbr> переменной.
|
||||
|
||||
Объявление типов для переменных позволяет улучшить поддержку вашего кода редакторами и различными инструментами.
|
||||
|
||||
Это просто **краткое руководство / напоминание** об аннотациях типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.
|
||||
|
||||
**FastAPI** целиком основан на аннотациях типов, у них много выгод и преимуществ.
|
||||
|
||||
Но даже если вы никогда не используете **FastAPI**, вам будет полезно немного узнать о них.
|
||||
|
||||
!!! note
|
||||
Если вы являетесь экспертом в Python и уже знаете всё об аннотациях типов, переходите к следующему разделу.
|
||||
|
||||
## Мотивация
|
||||
|
||||
Давайте начнем с простого примера:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
Вызов этой программы выводит:
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
Функция делает следующее:
|
||||
|
||||
* Принимает `first_name` и `last_name`.
|
||||
* Преобразует первую букву содержимого каждой переменной в верхний регистр с `title()`.
|
||||
* <abbr title="Объединяет в одно целое, последовательно, друг за другом.">Соединяет</abbr> их через пробел.
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Отредактируем пример
|
||||
|
||||
Это очень простая программа.
|
||||
|
||||
А теперь представьте, что вы пишете её с нуля.
|
||||
|
||||
В какой-то момент вы бы начали определение функции, у вас были бы готовы параметры...
|
||||
|
||||
Но затем вы должны вызвать «тот метод, который преобразует первую букву в верхний регистр».
|
||||
|
||||
Было это `upper`? Или `uppercase`? `first_uppercase`? `capitalize`?
|
||||
|
||||
Тогда вы попробуете с давним другом программиста: автодополнением редактора.
|
||||
|
||||
Вы вводите первый параметр функции, `first_name`, затем точку (`.`), а затем нажимаете `Ctrl+Space`, чтобы запустить дополнение.
|
||||
|
||||
Но, к сожалению, ничего полезного не выходит:
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### Добавим типы
|
||||
|
||||
Давайте изменим одну строчку в предыдущей версии.
|
||||
|
||||
Мы изменим именно этот фрагмент, параметры функции, с:
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
```
|
||||
|
||||
на:
|
||||
|
||||
```Python
|
||||
first_name: str, last_name: str
|
||||
```
|
||||
|
||||
Вот и все.
|
||||
|
||||
Это аннотации типов:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial002.py!}
|
||||
```
|
||||
|
||||
Это не то же самое, что объявление значений по умолчанию, например:
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
Это другая вещь.
|
||||
|
||||
Мы используем двоеточия (`:`), а не равно (`=`).
|
||||
|
||||
И добавление аннотаций типов обычно не меняет происходящего по сравнению с тем, что произошло бы без неё.
|
||||
|
||||
Но теперь представьте, что вы снова находитесь в процессе создания этой функции, но уже с аннотациями типов.
|
||||
|
||||
В тот же момент вы пытаетесь запустить автодополнение с помощью `Ctrl+Space` и вы видите:
|
||||
|
||||
<img src="/img/python-types/image02.png">
|
||||
|
||||
При этом вы можете просматривать варианты, пока не найдёте подходящий:
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
## Больше мотивации
|
||||
|
||||
Проверьте эту функцию, она уже имеет аннотации типов:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial003.py!}
|
||||
```
|
||||
|
||||
Поскольку редактор знает типы переменных, вы получаете не только дополнение, но и проверки ошибок:
|
||||
|
||||
<img src="/img/python-types/image04.png">
|
||||
|
||||
Теперь вы знаете, что вам нужно исправить, преобразовав `age` в строку с `str(age)`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/python_types/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Объявление типов
|
||||
|
||||
Вы только что видели основное место для объявления подсказок типов. В качестве параметров функции.
|
||||
|
||||
Это также основное место, где вы можете использовать их с **FastAPI**.
|
||||
|
||||
### Простые типы
|
||||
|
||||
Вы можете объявить все стандартные типы Python, а не только `str`.
|
||||
|
||||
Вы можете использовать, к примеру:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial005.py!}
|
||||
```
|
||||
|
||||
### Generic-типы с параметрами типов
|
||||
|
||||
Существуют некоторые структуры данных, которые могут содержать другие значения, например, `dict`, `list`, `set` и `tuple`. И внутренние значения тоже могут иметь свой тип.
|
||||
|
||||
Чтобы объявить эти типы и внутренние типы, вы можете использовать стандартный Python-модуль `typing`.
|
||||
|
||||
Он существует специально для поддержки подсказок этих типов.
|
||||
|
||||
#### `List`
|
||||
|
||||
Например, давайте определим переменную как `list`, состоящий из `str`.
|
||||
|
||||
Импортируйте `List` из `typing` (с заглавной `L`):
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Объявите переменную с тем же синтаксисом двоеточия (`:`).
|
||||
|
||||
В качестве типа укажите `List`.
|
||||
|
||||
Поскольку список является типом, содержащим некоторые внутренние типы, вы помещаете их в квадратные скобки:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Эти внутренние типы в квадратных скобках называются «параметрами типов».
|
||||
|
||||
В этом случае `str` является параметром типа, передаваемым в `List`.
|
||||
|
||||
Это означает: "переменная `items` является `list`, и каждый из элементов этого списка является `str`".
|
||||
|
||||
Если вы будете так поступать, редактор может оказывать поддержку даже при обработке элементов списка:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
Без типов добиться этого практически невозможно.
|
||||
|
||||
Обратите внимание, что переменная `item` является одним из элементов списка `items`.
|
||||
|
||||
И все же редактор знает, что это `str`, и поддерживает это.
|
||||
|
||||
#### `Tuple` и `Set`
|
||||
|
||||
Вы бы сделали то же самое, чтобы объявить `tuple` и `set`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
Это означает:
|
||||
|
||||
* Переменная `items_t` является `tuple` с 3 элементами: `int`, другим `int` и `str`.
|
||||
* Переменная `items_s` является `set` и каждый элемент имеет тип `bytes`.
|
||||
|
||||
#### `Dict`
|
||||
|
||||
Чтобы определить `dict`, вы передаёте 2 параметра типов, разделённых запятыми.
|
||||
|
||||
Первый параметр типа предназначен для ключей `dict`.
|
||||
|
||||
Второй параметр типа предназначен для значений `dict`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
Это означает:
|
||||
|
||||
* Переменная `prices` является `dict`:
|
||||
* Ключи этого `dict` имеют тип `str` (скажем, название каждого элемента).
|
||||
* Значения этого `dict` имеют тип `float` (скажем, цена каждой позиции).
|
||||
|
||||
#### `Optional`
|
||||
|
||||
Вы также можете использовать `Optional`, чтобы объявить, что переменная имеет тип, например, `str`, но это является «необязательным», что означает, что она также может быть `None`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Использование `Optional[str]` вместо просто `str` позволит редактору помочь вам в обнаружении ошибок, в которых вы могли бы предположить, что значение всегда является `str`, хотя на самом деле это может быть и `None`.
|
||||
|
||||
#### Generic-типы
|
||||
|
||||
Эти типы принимают параметры в квадратных скобках:
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Optional`
|
||||
* ...и др.
|
||||
|
||||
называются **Generic-типами** или **Generics**.
|
||||
|
||||
### Классы как типы
|
||||
|
||||
Вы также можете объявить класс как тип переменной.
|
||||
|
||||
Допустим, у вас есть класс `Person` с полем `name`:
|
||||
|
||||
```Python hl_lines="1-3"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
Тогда вы можете объявить переменную типа `Person`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
И снова вы получаете полную поддержку редактора:
|
||||
|
||||
<img src="/img/python-types/image06.png">
|
||||
|
||||
## Pydantic-модели
|
||||
|
||||
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> является Python-библиотекой для выполнения валидации данных.
|
||||
|
||||
Вы объявляете «форму» данных как классы с атрибутами.
|
||||
|
||||
И каждый атрибут имеет тип.
|
||||
|
||||
Затем вы создаете экземпляр этого класса с некоторыми значениями, и он проверяет значения, преобразует их в соответствующий тип (если все верно) и предоставляет вам объект со всеми данными.
|
||||
|
||||
И вы получаете полную поддержку редактора для этого итогового объекта.
|
||||
|
||||
Взято из официальной документации Pydantic:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Чтобы узнать больше о <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic, читайте его документацию</a>.
|
||||
|
||||
**FastAPI** целиком основан на Pydantic.
|
||||
|
||||
Вы увидите намного больше всего этого на практике в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Аннотации типов в **FastAPI**
|
||||
|
||||
**FastAPI** получает преимущества аннотаций типов для выполнения определённых задач.
|
||||
|
||||
С **FastAPI** вы объявляете параметры с аннотациями типов и получаете:
|
||||
|
||||
* **Поддержку редактора**.
|
||||
* **Проверки типов**.
|
||||
|
||||
...и **FastAPI** использует тот же механизм для:
|
||||
|
||||
* **Определения требований**: из параметров пути запроса, параметров запроса, заголовков, зависимостей и т.д.
|
||||
* **Преобразования данных**: от запроса к нужному типу.
|
||||
* **Валидации данных**: исходя из каждого запроса:
|
||||
* Генерации **автоматических ошибок**, возвращаемых клиенту, когда данные не являются корректными.
|
||||
* **Документирования** API с использованием OpenAPI:
|
||||
* который затем используется пользовательскими интерфейсами автоматической интерактивной документации.
|
||||
|
||||
Всё это может показаться абстрактным. Не волнуйтесь. Вы увидите всё это в действии в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
Важно то, что при использовании стандартных типов Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.) **FastAPI** сделает за вас большую часть работы.
|
||||
|
||||
!!! info
|
||||
Если вы уже прошли всё руководство и вернулись, чтобы узнать больше о типах, хорошим ресурсом является <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">«шпаргалка» от `mypy`</a>.
|
||||
@@ -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:
|
||||
@@ -76,7 +77,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
@@ -76,7 +77,7 @@ extra:
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
@@ -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
|
||||
|
||||