committed by
GitHub
72 changed files with 3417 additions and 2653 deletions
@ -1,314 +0,0 @@ |
|||
# ↔ 🗄 |
|||
|
|||
!!! warning |
|||
👉 👍 🏧 ⚒. 👆 🎲 💪 🚶 ⚫️. |
|||
|
|||
🚥 👆 📄 🔰 - 👩💻 🦮, 👆 💪 🎲 🚶 👉 📄. |
|||
|
|||
🚥 👆 ⏪ 💭 👈 👆 💪 🔀 🏗 🗄 🔗, 😣 👂. |
|||
|
|||
📤 💼 🌐❔ 👆 💪 💪 🔀 🏗 🗄 🔗. |
|||
|
|||
👉 📄 👆 🔜 👀 ❔. |
|||
|
|||
## 😐 🛠️ |
|||
|
|||
😐 (🔢) 🛠️, ⏩. |
|||
|
|||
`FastAPI` 🈸 (👐) ✔️ `.openapi()` 👩🔬 👈 📈 📨 🗄 🔗. |
|||
|
|||
🍕 🈸 🎚 🏗, *➡ 🛠️* `/openapi.json` (⚖️ ⚫️❔ 👆 ⚒ 👆 `openapi_url`) ®. |
|||
|
|||
⚫️ 📨 🎻 📨 ⏮️ 🏁 🈸 `.openapi()` 👩🔬. |
|||
|
|||
🔢, ⚫️❔ 👩🔬 `.openapi()` 🔨 ✅ 🏠 `.openapi_schema` 👀 🚥 ⚫️ ✔️ 🎚 & 📨 👫. |
|||
|
|||
🚥 ⚫️ 🚫, ⚫️ 🏗 👫 ⚙️ 🚙 🔢 `fastapi.openapi.utils.get_openapi`. |
|||
|
|||
& 👈 🔢 `get_openapi()` 📨 🔢: |
|||
|
|||
* `title`: 🗄 📛, 🎦 🩺. |
|||
* `version`: ⏬ 👆 🛠️, ✅ `2.5.0`. |
|||
* `openapi_version`: ⏬ 🗄 🔧 ⚙️. 🔢, ⏪: `3.0.2`. |
|||
* `description`: 📛 👆 🛠️. |
|||
* `routes`: 📇 🛣, 👫 🔠 ® *➡ 🛠️*. 👫 ✊ ⚪️➡️ `app.routes`. |
|||
|
|||
## 🔑 🔢 |
|||
|
|||
⚙️ ℹ 🔛, 👆 💪 ⚙️ 🎏 🚙 🔢 🏗 🗄 🔗 & 🔐 🔠 🍕 👈 👆 💪. |
|||
|
|||
🖼, ➡️ 🚮 <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">📄 🗄 ↔ 🔌 🛃 🔱</a>. |
|||
|
|||
### 😐 **FastAPI** |
|||
|
|||
🥇, ✍ 🌐 👆 **FastAPI** 🈸 🛎: |
|||
|
|||
```Python hl_lines="1 4 7-9" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 🏗 🗄 🔗 |
|||
|
|||
⤴️, ⚙️ 🎏 🚙 🔢 🏗 🗄 🔗, 🔘 `custom_openapi()` 🔢: |
|||
|
|||
```Python hl_lines="2 15-20" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 🔀 🗄 🔗 |
|||
|
|||
🔜 👆 💪 🚮 📄 ↔, ❎ 🛃 `x-logo` `info` "🎚" 🗄 🔗: |
|||
|
|||
```Python hl_lines="21-23" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 💾 🗄 🔗 |
|||
|
|||
👆 💪 ⚙️ 🏠 `.openapi_schema` "💾", 🏪 👆 🏗 🔗. |
|||
|
|||
👈 🌌, 👆 🈸 🏆 🚫 ✔️ 🏗 🔗 🔠 🕰 👩💻 📂 👆 🛠️ 🩺. |
|||
|
|||
⚫️ 🔜 🏗 🕴 🕐, & ⤴️ 🎏 💾 🔗 🔜 ⚙️ ⏭ 📨. |
|||
|
|||
```Python hl_lines="13-14 24-25" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 🔐 👩🔬 |
|||
|
|||
🔜 👆 💪 ❎ `.openapi()` 👩🔬 ⏮️ 👆 🆕 🔢. |
|||
|
|||
```Python hl_lines="28" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### ✅ ⚫️ |
|||
|
|||
🕐 👆 🚶 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> 👆 🔜 👀 👈 👆 ⚙️ 👆 🛃 🔱 (👉 🖼, **FastAPI**'Ⓜ 🔱): |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image01.png"> |
|||
|
|||
## 👤-🕸 🕸 & 🎚 🩺 |
|||
|
|||
🛠️ 🩺 ⚙️ **🦁 🎚** & **📄**, & 🔠 👈 💪 🕸 & 🎚 📁. |
|||
|
|||
🔢, 👈 📁 🍦 ⚪️➡️ <abbr title="Content Delivery Network: A service, normally composed of several servers, that provides static files, like JavaScript and CSS. It's commonly used to serve those files from the server closer to the client, improving performance.">💲</abbr>. |
|||
|
|||
✋️ ⚫️ 💪 🛃 ⚫️, 👆 💪 ⚒ 🎯 💲, ⚖️ 🍦 📁 👆. |
|||
|
|||
👈 ⚠, 🖼, 🚥 👆 💪 👆 📱 🚧 👷 ⏪ 📱, 🍵 📂 🕸 🔐, ⚖️ 🇧🇿 🕸. |
|||
|
|||
📥 👆 🔜 👀 ❔ 🍦 👈 📁 👆, 🎏 FastAPI 📱, & 🔗 🩺 ⚙️ 👫. |
|||
|
|||
### 🏗 📁 📊 |
|||
|
|||
➡️ 💬 👆 🏗 📁 📊 👀 💖 👉: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
``` |
|||
|
|||
🔜 ✍ 📁 🏪 📚 🎻 📁. |
|||
|
|||
👆 🆕 📁 📊 💪 👀 💖 👉: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
└── static/ |
|||
``` |
|||
|
|||
### ⏬ 📁 |
|||
|
|||
⏬ 🎻 📁 💪 🩺 & 🚮 👫 🔛 👈 `static/` 📁. |
|||
|
|||
👆 💪 🎲 ▶️️-🖊 🔠 🔗 & 🖊 🎛 🎏 `Save link as...`. |
|||
|
|||
**🦁 🎚** ⚙️ 📁: |
|||
|
|||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a> |
|||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a> |
|||
|
|||
& **📄** ⚙️ 📁: |
|||
|
|||
* <a href="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a> |
|||
|
|||
⏮️ 👈, 👆 📁 📊 💪 👀 💖: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
└── static |
|||
├── redoc.standalone.js |
|||
├── swagger-ui-bundle.js |
|||
└── swagger-ui.css |
|||
``` |
|||
|
|||
### 🍦 🎻 📁 |
|||
|
|||
* 🗄 `StaticFiles`. |
|||
* "🗻" `StaticFiles()` 👐 🎯 ➡. |
|||
|
|||
```Python hl_lines="7 11" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
### 💯 🎻 📁 |
|||
|
|||
▶️ 👆 🈸 & 🚶 <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>. |
|||
|
|||
👆 🔜 👀 📶 📏 🕸 📁 **📄**. |
|||
|
|||
⚫️ 💪 ▶️ ⏮️ 🕳 💖: |
|||
|
|||
```JavaScript |
|||
/*! |
|||
* ReDoc - OpenAPI/Swagger-generated API Reference Documentation |
|||
* ------------------------------------------------------------- |
|||
* Version: "2.0.0-rc.18" |
|||
* Repo: https://github.com/Redocly/redoc |
|||
*/ |
|||
!function(e,t){"object"==typeof exports&&"object"==typeof m |
|||
|
|||
... |
|||
``` |
|||
|
|||
👈 ✔ 👈 👆 💆♂ 💪 🍦 🎻 📁 ⚪️➡️ 👆 📱, & 👈 👆 🥉 🎻 📁 🩺 ☑ 🥉. |
|||
|
|||
🔜 👥 💪 🔗 📱 ⚙️ 📚 🎻 📁 🩺. |
|||
|
|||
### ❎ 🏧 🩺 |
|||
|
|||
🥇 🔁 ❎ 🏧 🩺, 📚 ⚙️ 💲 🔢. |
|||
|
|||
❎ 👫, ⚒ 👫 📛 `None` 🕐❔ 🏗 👆 `FastAPI` 📱: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
### 🔌 🛃 🩺 |
|||
|
|||
🔜 👆 💪 ✍ *➡ 🛠️* 🛃 🩺. |
|||
|
|||
👆 💪 🏤-⚙️ FastAPI 🔗 🔢 ✍ 🕸 📃 🩺, & 🚶♀️ 👫 💪 ❌: |
|||
|
|||
* `openapi_url`: 📛 🌐❔ 🕸 📃 🩺 💪 🤚 🗄 🔗 👆 🛠️. 👆 💪 ⚙️ 📥 🔢 `app.openapi_url`. |
|||
* `title`: 📛 👆 🛠️. |
|||
* `oauth2_redirect_url`: 👆 💪 ⚙️ `app.swagger_ui_oauth2_redirect_url` 📥 ⚙️ 🔢. |
|||
* `swagger_js_url`: 📛 🌐❔ 🕸 👆 🦁 🎚 🩺 💪 🤚 **🕸** 📁. 👉 1️⃣ 👈 👆 👍 📱 🔜 🍦. |
|||
* `swagger_css_url`: 📛 🌐❔ 🕸 👆 🦁 🎚 🩺 💪 🤚 **🎚** 📁. 👉 1️⃣ 👈 👆 👍 📱 🔜 🍦. |
|||
|
|||
& ➡ 📄... |
|||
|
|||
```Python hl_lines="2-6 14-22 25-27 30-36" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! tip |
|||
*➡ 🛠️* `swagger_ui_redirect` 👩🎓 🕐❔ 👆 ⚙️ Oauth2️⃣. |
|||
|
|||
🚥 👆 🛠️ 👆 🛠️ ⏮️ Oauth2️⃣ 🐕🦺, 👆 🔜 💪 🔓 & 👟 🔙 🛠️ 🩺 ⏮️ 📎 🎓. & 🔗 ⏮️ ⚫️ ⚙️ 🎰 Oauth2️⃣ 🤝. |
|||
|
|||
🦁 🎚 🔜 🍵 ⚫️ ⛅ 🎑 👆, ✋️ ⚫️ 💪 👉 "❎" 👩🎓. |
|||
|
|||
### ✍ *➡ 🛠️* 💯 ⚫️ |
|||
|
|||
🔜, 💪 💯 👈 🌐 👷, ✍ *➡ 🛠️*: |
|||
|
|||
```Python hl_lines="39-41" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
### 💯 ⚫️ |
|||
|
|||
🔜, 👆 🔜 💪 🔌 👆 📻, 🚶 👆 🩺 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, & 🔃 📃. |
|||
|
|||
& 🍵 🕸, 👆 🔜 💪 👀 🩺 👆 🛠️ & 🔗 ⏮️ ⚫️. |
|||
|
|||
## 🛠️ 🦁 🎚 |
|||
|
|||
👆 💪 🔗 ➕ <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">🦁 🎚 🔢</a>. |
|||
|
|||
🔗 👫, 🚶♀️ `swagger_ui_parameters` ❌ 🕐❔ 🏗 `FastAPI()` 📱 🎚 ⚖️ `get_swagger_ui_html()` 🔢. |
|||
|
|||
`swagger_ui_parameters` 📨 📖 ⏮️ 📳 🚶♀️ 🦁 🎚 🔗. |
|||
|
|||
FastAPI 🗜 📳 **🎻** ⚒ 👫 🔗 ⏮️ 🕸, 👈 ⚫️❔ 🦁 🎚 💪. |
|||
|
|||
### ❎ ❕ 🎦 |
|||
|
|||
🖼, 👆 💪 ❎ ❕ 🎦 🦁 🎚. |
|||
|
|||
🍵 🔀 ⚒, ❕ 🎦 🛠️ 🔢: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image02.png"> |
|||
|
|||
✋️ 👆 💪 ❎ ⚫️ ⚒ `syntaxHighlight` `False`: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/extending_openapi/tutorial003.py!} |
|||
``` |
|||
|
|||
...& ⤴️ 🦁 🎚 🏆 🚫 🎦 ❕ 🎦 🚫🔜: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image03.png"> |
|||
|
|||
### 🔀 🎢 |
|||
|
|||
🎏 🌌 👆 💪 ⚒ ❕ 🎦 🎢 ⏮️ 🔑 `"syntaxHighlight.theme"` (👀 👈 ⚫️ ✔️ ❣ 🖕): |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/extending_openapi/tutorial004.py!} |
|||
``` |
|||
|
|||
👈 📳 🔜 🔀 ❕ 🎦 🎨 🎢: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image04.png"> |
|||
|
|||
### 🔀 🔢 🦁 🎚 🔢 |
|||
|
|||
FastAPI 🔌 🔢 📳 🔢 ☑ 🌅 ⚙️ 💼. |
|||
|
|||
⚫️ 🔌 👫 🔢 📳: |
|||
|
|||
```Python |
|||
{!../../../fastapi/openapi/docs.py[ln:7-13]!} |
|||
``` |
|||
|
|||
👆 💪 🔐 🙆 👫 ⚒ 🎏 💲 ❌ `swagger_ui_parameters`. |
|||
|
|||
🖼, ❎ `deepLinking` 👆 💪 🚶♀️ 👉 ⚒ `swagger_ui_parameters`: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/extending_openapi/tutorial005.py!} |
|||
``` |
|||
|
|||
### 🎏 🦁 🎚 🔢 |
|||
|
|||
👀 🌐 🎏 💪 📳 👆 💪 ⚙️, ✍ 🛂 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">🩺 🦁 🎚 🔢</a>. |
|||
|
|||
### 🕸-🕴 ⚒ |
|||
|
|||
🦁 🎚 ✔ 🎏 📳 **🕸-🕴** 🎚 (🖼, 🕸 🔢). |
|||
|
|||
FastAPI 🔌 👫 🕸-🕴 `presets` ⚒: |
|||
|
|||
```JavaScript |
|||
presets: [ |
|||
SwaggerUIBundle.presets.apis, |
|||
SwaggerUIBundle.SwaggerUIStandalonePreset |
|||
] |
|||
``` |
|||
|
|||
👫 **🕸** 🎚, 🚫 🎻, 👆 💪 🚫 🚶♀️ 👫 ⚪️➡️ 🐍 📟 🔗. |
|||
|
|||
🚥 👆 💪 ⚙️ 🕸-🕴 📳 💖 📚, 👆 💪 ⚙️ 1️⃣ 👩🔬 🔛. 🔐 🌐 🦁 🎚 *➡ 🛠️* & ❎ ✍ 🙆 🕸 👆 💪. |
@ -1,258 +0,0 @@ |
|||
# 🛠️ FastAPI 🔛 🪔 |
|||
|
|||
👉 📄 👆 🔜 💡 ❔ 💪 🛠️ **FastAPI** 🈸 🔛 <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">🪔</a> ⚙️ 🆓 📄. 👶 |
|||
|
|||
⚫️ 🔜 ✊ 👆 🔃 **1️⃣0️⃣ ⏲**. |
|||
|
|||
!!! info |
|||
<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">🪔</a> **FastAPI** 💰. 👶 |
|||
|
|||
## 🔰 **FastAPI** 📱 |
|||
|
|||
* ✍ 📁 👆 📱, 🖼, `./fastapideta/` & ⛔ 🔘 ⚫️. |
|||
|
|||
### FastAPI 📟 |
|||
|
|||
* ✍ `main.py` 📁 ⏮️: |
|||
|
|||
```Python |
|||
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): |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
### 📄 |
|||
|
|||
🔜, 🎏 📁 ✍ 📁 `requirements.txt` ⏮️: |
|||
|
|||
```text |
|||
fastapi |
|||
``` |
|||
|
|||
!!! tip |
|||
👆 🚫 💪 ❎ Uvicorn 🛠️ 🔛 🪔, 👐 👆 🔜 🎲 💚 ❎ ⚫️ 🌐 💯 👆 📱. |
|||
|
|||
### 📁 📊 |
|||
|
|||
👆 🔜 🔜 ✔️ 1️⃣ 📁 `./fastapideta/` ⏮️ 2️⃣ 📁: |
|||
|
|||
``` |
|||
. |
|||
└── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
## ✍ 🆓 🪔 🏧 |
|||
|
|||
🔜 ✍ <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">🆓 🏧 🔛 🪔</a>, 👆 💪 📧 & 🔐. |
|||
|
|||
👆 🚫 💪 💳. |
|||
|
|||
## ❎ ✳ |
|||
|
|||
🕐 👆 ✔️ 👆 🏧, ❎ 🪔 <abbr title="Command Line Interface application">✳</abbr>: |
|||
|
|||
=== "💾, 🇸🇻" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ curl -fsSL https://get.deta.dev/cli.sh | sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "🚪 📋" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ iwr https://get.deta.dev/cli.ps1 -useb | iex |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
⏮️ ❎ ⚫️, 📂 🆕 📶 👈 ❎ ✳ 🔍. |
|||
|
|||
🆕 📶, ✔ 👈 ⚫️ ☑ ❎ ⏮️: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta --help |
|||
|
|||
Deta command line interface for managing deta micros. |
|||
Complete documentation available at https://docs.deta.sh |
|||
|
|||
Usage: |
|||
deta [flags] |
|||
deta [command] |
|||
|
|||
Available Commands: |
|||
auth Change auth settings for a deta micro |
|||
|
|||
... |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip |
|||
🚥 👆 ✔️ ⚠ ❎ ✳, ✅ <a href="https://docs.deta.sh/docs/micros/getting_started?ref=fastapi" class="external-link" target="_blank">🛂 🪔 🩺</a>. |
|||
|
|||
## 💳 ⏮️ ✳ |
|||
|
|||
🔜 💳 🪔 ⚪️➡️ ✳ ⏮️: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta login |
|||
|
|||
Please, log in from the web page. Waiting.. |
|||
Logged in successfully. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
👉 🔜 📂 🕸 🖥 & 🔓 🔁. |
|||
|
|||
## 🛠️ ⏮️ 🪔 |
|||
|
|||
⏭, 🛠️ 👆 🈸 ⏮️ 🪔 ✳: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta new |
|||
|
|||
Successfully created a new micro |
|||
|
|||
// Notice the "endpoint" 🔍 |
|||
|
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
|
|||
Adding dependencies... |
|||
|
|||
|
|||
---> 100% |
|||
|
|||
|
|||
Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
👆 🔜 👀 🎻 📧 🎏: |
|||
|
|||
```JSON hl_lines="4" |
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
``` |
|||
|
|||
!!! tip |
|||
👆 🛠️ 🔜 ✔️ 🎏 `"endpoint"` 📛. |
|||
|
|||
## ✅ ⚫️ |
|||
|
|||
🔜 📂 👆 🖥 👆 `endpoint` 📛. 🖼 🔛 ⚫️ `https://qltnci.deta.dev`, ✋️ 👆 🔜 🎏. |
|||
|
|||
👆 🔜 👀 🎻 📨 ⚪️➡️ 👆 FastAPI 📱: |
|||
|
|||
```JSON |
|||
{ |
|||
"Hello": "World" |
|||
} |
|||
``` |
|||
|
|||
& 🔜 🚶 `/docs` 👆 🛠️, 🖼 🔛 ⚫️ 🔜 `https://qltnci.deta.dev/docs`. |
|||
|
|||
⚫️ 🔜 🎦 👆 🩺 💖: |
|||
|
|||
<img src="/img/deployment/deta/image01.png"> |
|||
|
|||
## 🛠️ 📢 🔐 |
|||
|
|||
🔢, 🪔 🔜 🍵 🤝 ⚙️ 🍪 👆 🏧. |
|||
|
|||
✋️ 🕐 👆 🔜, 👆 💪 ⚒ ⚫️ 📢 ⏮️: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta auth disable |
|||
|
|||
Successfully disabled http auth |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
🔜 👆 💪 💰 👈 📛 ⏮️ 🙆 & 👫 🔜 💪 🔐 👆 🛠️. 👶 |
|||
|
|||
## 🇺🇸🔍 |
|||
|
|||
㊗ ❗ 👆 🛠️ 👆 FastAPI 📱 🪔 ❗ 👶 👶 |
|||
|
|||
, 👀 👈 🪔 ☑ 🍵 🇺🇸🔍 👆, 👆 🚫 ✔️ ✊ 💅 👈 & 💪 💭 👈 👆 👩💻 🔜 ✔️ 🔐 🗜 🔗. 👶 👶 |
|||
|
|||
## ✅ 🕶 |
|||
|
|||
⚪️➡️ 👆 🩺 🎚 (👫 🔜 📛 💖 `https://qltnci.deta.dev/docs`) 📨 📨 👆 *➡ 🛠️* `/items/{item_id}`. |
|||
|
|||
🖼 ⏮️ 🆔 `5`. |
|||
|
|||
🔜 🚶 <a href="https://web.deta.sh/" class="external-link" target="_blank">https://web.deta.sh</a>. |
|||
|
|||
👆 🔜 👀 📤 📄 ◀️ 🤙 <abbr title="it comes from Micro(server)">"◾"</abbr> ⏮️ 🔠 👆 📱. |
|||
|
|||
👆 🔜 👀 📑 ⏮️ "ℹ", & 📑 "🕶", 🚶 📑 "🕶". |
|||
|
|||
📤 👆 💪 ✔ ⏮️ 📨 📨 👆 📱. |
|||
|
|||
👆 💪 ✍ 👫 & 🏤-🤾 👫. |
|||
|
|||
<img src="/img/deployment/deta/image02.png"> |
|||
|
|||
## 💡 🌅 |
|||
|
|||
☝, 👆 🔜 🎲 💚 🏪 💽 👆 📱 🌌 👈 😣 🔘 🕰. 👈 👆 💪 ⚙️ <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">🪔 🧢</a>, ⚫️ ✔️ 👍 **🆓 🎚**. |
|||
|
|||
👆 💪 ✍ 🌅 <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">🪔 🩺</a>. |
|||
|
|||
## 🛠️ 🔧 |
|||
|
|||
👟 🔙 🔧 👥 🔬 [🛠️ 🔧](./concepts.md){.internal-link target=_blank}, 📥 ❔ 🔠 👫 🔜 🍵 ⏮️ 🪔: |
|||
|
|||
* **🇺🇸🔍**: 🍵 🪔, 👫 🔜 🤝 👆 📁 & 🍵 🇺🇸🔍 🔁. |
|||
* **🏃♂ 🔛 🕴**: 🍵 🪔, 🍕 👫 🐕🦺. |
|||
* **⏏**: 🍵 🪔, 🍕 👫 🐕🦺. |
|||
* **🧬**: 🍵 🪔, 🍕 👫 🐕🦺. |
|||
* **💾**: 📉 🔁 🪔, 👆 💪 📧 👫 📈 ⚫️. |
|||
* **⏮️ 🔁 ⏭ ▶️**: 🚫 🔗 🐕🦺, 👆 💪 ⚒ ⚫️ 👷 ⏮️ 👫 💾 ⚙️ ⚖️ 🌖 ✍. |
|||
|
|||
!!! note |
|||
🪔 🔧 ⚒ ⚫️ ⏩ (& 🆓) 🛠️ 🙅 🈸 🔜. |
|||
|
|||
⚫️ 💪 📉 📚 ⚙️ 💼, ✋️ 🎏 🕰, ⚫️ 🚫 🐕🦺 🎏, 💖 ⚙️ 🔢 💽 (↖️ ⚪️➡️ 🪔 👍 ☁ 💽 ⚙️), 🛃 🕹 🎰, ♒️. |
|||
|
|||
👆 💪 ✍ 🌅 ℹ <a href="https://docs.deta.sh/docs/micros/about/" class="external-link" target="_blank">🪔 🩺</a> 👀 🚥 ⚫️ ▶️️ ⚒ 👆. |
@ -0,0 +1,90 @@ |
|||
# ↔ 🗄 |
|||
|
|||
!!! warning |
|||
👉 👍 🏧 ⚒. 👆 🎲 💪 🚶 ⚫️. |
|||
|
|||
🚥 👆 📄 🔰 - 👩💻 🦮, 👆 💪 🎲 🚶 👉 📄. |
|||
|
|||
🚥 👆 ⏪ 💭 👈 👆 💪 🔀 🏗 🗄 🔗, 😣 👂. |
|||
|
|||
📤 💼 🌐❔ 👆 💪 💪 🔀 🏗 🗄 🔗. |
|||
|
|||
👉 📄 👆 🔜 👀 ❔. |
|||
|
|||
## 😐 🛠️ |
|||
|
|||
😐 (🔢) 🛠️, ⏩. |
|||
|
|||
`FastAPI` 🈸 (👐) ✔️ `.openapi()` 👩🔬 👈 📈 📨 🗄 🔗. |
|||
|
|||
🍕 🈸 🎚 🏗, *➡ 🛠️* `/openapi.json` (⚖️ ⚫️❔ 👆 ⚒ 👆 `openapi_url`) ®. |
|||
|
|||
⚫️ 📨 🎻 📨 ⏮️ 🏁 🈸 `.openapi()` 👩🔬. |
|||
|
|||
🔢, ⚫️❔ 👩🔬 `.openapi()` 🔨 ✅ 🏠 `.openapi_schema` 👀 🚥 ⚫️ ✔️ 🎚 & 📨 👫. |
|||
|
|||
🚥 ⚫️ 🚫, ⚫️ 🏗 👫 ⚙️ 🚙 🔢 `fastapi.openapi.utils.get_openapi`. |
|||
|
|||
& 👈 🔢 `get_openapi()` 📨 🔢: |
|||
|
|||
* `title`: 🗄 📛, 🎦 🩺. |
|||
* `version`: ⏬ 👆 🛠️, ✅ `2.5.0`. |
|||
* `openapi_version`: ⏬ 🗄 🔧 ⚙️. 🔢, ⏪: `3.0.2`. |
|||
* `description`: 📛 👆 🛠️. |
|||
* `routes`: 📇 🛣, 👫 🔠 ® *➡ 🛠️*. 👫 ✊ ⚪️➡️ `app.routes`. |
|||
|
|||
## 🔑 🔢 |
|||
|
|||
⚙️ ℹ 🔛, 👆 💪 ⚙️ 🎏 🚙 🔢 🏗 🗄 🔗 & 🔐 🔠 🍕 👈 👆 💪. |
|||
|
|||
🖼, ➡️ 🚮 <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">📄 🗄 ↔ 🔌 🛃 🔱</a>. |
|||
|
|||
### 😐 **FastAPI** |
|||
|
|||
🥇, ✍ 🌐 👆 **FastAPI** 🈸 🛎: |
|||
|
|||
```Python hl_lines="1 4 7-9" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 🏗 🗄 🔗 |
|||
|
|||
⤴️, ⚙️ 🎏 🚙 🔢 🏗 🗄 🔗, 🔘 `custom_openapi()` 🔢: |
|||
|
|||
```Python hl_lines="2 15-20" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 🔀 🗄 🔗 |
|||
|
|||
🔜 👆 💪 🚮 📄 ↔, ❎ 🛃 `x-logo` `info` "🎚" 🗄 🔗: |
|||
|
|||
```Python hl_lines="21-23" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 💾 🗄 🔗 |
|||
|
|||
👆 💪 ⚙️ 🏠 `.openapi_schema` "💾", 🏪 👆 🏗 🔗. |
|||
|
|||
👈 🌌, 👆 🈸 🏆 🚫 ✔️ 🏗 🔗 🔠 🕰 👩💻 📂 👆 🛠️ 🩺. |
|||
|
|||
⚫️ 🔜 🏗 🕴 🕐, & ⤴️ 🎏 💾 🔗 🔜 ⚙️ ⏭ 📨. |
|||
|
|||
```Python hl_lines="13-14 24-25" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### 🔐 👩🔬 |
|||
|
|||
🔜 👆 💪 ❎ `.openapi()` 👩🔬 ⏮️ 👆 🆕 🔢. |
|||
|
|||
```Python hl_lines="28" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### ✅ ⚫️ |
|||
|
|||
🕐 👆 🚶 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> 👆 🔜 👀 👈 👆 ⚙️ 👆 🛃 🔱 (👉 🖼, **FastAPI**'Ⓜ 🔱): |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image01.png"> |
@ -1,318 +0,0 @@ |
|||
# Extending OpenAPI |
|||
|
|||
!!! warning |
|||
This is a rather advanced feature. You probably can skip it. |
|||
|
|||
If you are just following the tutorial - user guide, you can probably skip this section. |
|||
|
|||
If you already know that you need to modify the generated OpenAPI schema, continue reading. |
|||
|
|||
There are some cases where you might need to modify the generated OpenAPI schema. |
|||
|
|||
In this section you will see how. |
|||
|
|||
## The normal process |
|||
|
|||
The normal (default) process, is as follows. |
|||
|
|||
A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema. |
|||
|
|||
As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered. |
|||
|
|||
It just returns a JSON response with the result of the application's `.openapi()` method. |
|||
|
|||
By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them. |
|||
|
|||
If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`. |
|||
|
|||
And that function `get_openapi()` receives as parameters: |
|||
|
|||
* `title`: The OpenAPI title, shown in the docs. |
|||
* `version`: The version of your API, e.g. `2.5.0`. |
|||
* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.1.0`. |
|||
* `summary`: A short summary of the API. |
|||
* `description`: The description of your API, this can include markdown and will be shown in the docs. |
|||
* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`. |
|||
|
|||
!!! info |
|||
The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by FastAPI 0.99.0 and above. |
|||
|
|||
## Overriding the defaults |
|||
|
|||
Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need. |
|||
|
|||
For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">ReDoc's OpenAPI extension to include a custom logo</a>. |
|||
|
|||
### Normal **FastAPI** |
|||
|
|||
First, write all your **FastAPI** application as normally: |
|||
|
|||
```Python hl_lines="1 4 7-9" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Generate the OpenAPI schema |
|||
|
|||
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function: |
|||
|
|||
```Python hl_lines="2 15-21" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Modify the OpenAPI schema |
|||
|
|||
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema: |
|||
|
|||
```Python hl_lines="22-24" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Cache the OpenAPI schema |
|||
|
|||
You can use the property `.openapi_schema` as a "cache", to store your generated schema. |
|||
|
|||
That way, your application won't have to generate the schema every time a user opens your API docs. |
|||
|
|||
It will be generated only once, and then the same cached schema will be used for the next requests. |
|||
|
|||
```Python hl_lines="13-14 25-26" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Override the method |
|||
|
|||
Now you can replace the `.openapi()` method with your new function. |
|||
|
|||
```Python hl_lines="29" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Check it |
|||
|
|||
Once you go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> you will see that you are using your custom logo (in this example, **FastAPI**'s logo): |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image01.png"> |
|||
|
|||
## Self-hosting JavaScript and CSS for docs |
|||
|
|||
The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files. |
|||
|
|||
By default, those files are served from a <abbr title="Content Delivery Network: A service, normally composed of several servers, that provides static files, like JavaScript and CSS. It's commonly used to serve those files from the server closer to the client, improving performance.">CDN</abbr>. |
|||
|
|||
But it's possible to customize it, you can set a specific CDN, or serve the files yourself. |
|||
|
|||
That's useful, for example, if you need your app to keep working even while offline, without open Internet access, or in a local network. |
|||
|
|||
Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them. |
|||
|
|||
### Project file structure |
|||
|
|||
Let's say your project file structure looks like this: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
``` |
|||
|
|||
Now create a directory to store those static files. |
|||
|
|||
Your new file structure could look like this: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
└── static/ |
|||
``` |
|||
|
|||
### Download the files |
|||
|
|||
Download the static files needed for the docs and put them on that `static/` directory. |
|||
|
|||
You can probably right-click each link and select an option similar to `Save link as...`. |
|||
|
|||
**Swagger UI** uses the files: |
|||
|
|||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a> |
|||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a> |
|||
|
|||
And **ReDoc** uses the file: |
|||
|
|||
* <a href="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a> |
|||
|
|||
After that, your file structure could look like: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
└── static |
|||
├── redoc.standalone.js |
|||
├── swagger-ui-bundle.js |
|||
└── swagger-ui.css |
|||
``` |
|||
|
|||
### Serve the static files |
|||
|
|||
* Import `StaticFiles`. |
|||
* "Mount" a `StaticFiles()` instance in a specific path. |
|||
|
|||
```Python hl_lines="7 11" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
### Test the static files |
|||
|
|||
Start your application and go to <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>. |
|||
|
|||
You should see a very long JavaScript file for **ReDoc**. |
|||
|
|||
It could start with something like: |
|||
|
|||
```JavaScript |
|||
/*! |
|||
* ReDoc - OpenAPI/Swagger-generated API Reference Documentation |
|||
* ------------------------------------------------------------- |
|||
* Version: "2.0.0-rc.18" |
|||
* Repo: https://github.com/Redocly/redoc |
|||
*/ |
|||
!function(e,t){"object"==typeof exports&&"object"==typeof m |
|||
|
|||
... |
|||
``` |
|||
|
|||
That confirms that you are being able to serve static files from your app, and that you placed the static files for the docs in the correct place. |
|||
|
|||
Now we can configure the app to use those static files for the docs. |
|||
|
|||
### Disable the automatic docs |
|||
|
|||
The first step is to disable the automatic docs, as those use the CDN by default. |
|||
|
|||
To disable them, set their URLs to `None` when creating your `FastAPI` app: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
### Include the custom docs |
|||
|
|||
Now you can create the *path operations* for the custom docs. |
|||
|
|||
You can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: |
|||
|
|||
* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. |
|||
* `title`: the title of your API. |
|||
* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. |
|||
* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. This is the one that your own app is now serving. |
|||
* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. This is the one that your own app is now serving. |
|||
|
|||
And similarly for ReDoc... |
|||
|
|||
```Python hl_lines="2-6 14-22 25-27 30-36" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! tip |
|||
The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. |
|||
|
|||
If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. |
|||
|
|||
Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. |
|||
|
|||
### Create a *path operation* to test it |
|||
|
|||
Now, to be able to test that everything works, create a *path operation*: |
|||
|
|||
```Python hl_lines="39-41" |
|||
{!../../../docs_src/extending_openapi/tutorial002.py!} |
|||
``` |
|||
|
|||
### Test it |
|||
|
|||
Now, you should be able to disconnect your WiFi, go to your docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, and reload the page. |
|||
|
|||
And even without Internet, you would be able to see the docs for your API and interact with it. |
|||
|
|||
## Configuring Swagger UI |
|||
|
|||
You can configure some extra <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">Swagger UI parameters</a>. |
|||
|
|||
To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. |
|||
|
|||
`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. |
|||
|
|||
FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. |
|||
|
|||
### Disable Syntax Highlighting |
|||
|
|||
For example, you could disable syntax highlighting in Swagger UI. |
|||
|
|||
Without changing the settings, syntax highlighting is enabled by default: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image02.png"> |
|||
|
|||
But you can disable it by setting `syntaxHighlight` to `False`: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/extending_openapi/tutorial003.py!} |
|||
``` |
|||
|
|||
...and then Swagger UI won't show the syntax highlighting anymore: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image03.png"> |
|||
|
|||
### Change the Theme |
|||
|
|||
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/extending_openapi/tutorial004.py!} |
|||
``` |
|||
|
|||
That configuration would change the syntax highlighting color theme: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image04.png"> |
|||
|
|||
### Change Default Swagger UI Parameters |
|||
|
|||
FastAPI includes some default configuration parameters appropriate for most of the use cases. |
|||
|
|||
It includes these default configurations: |
|||
|
|||
```Python |
|||
{!../../../fastapi/openapi/docs.py[ln:7-13]!} |
|||
``` |
|||
|
|||
You can override any of them by setting a different value in the argument `swagger_ui_parameters`. |
|||
|
|||
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/extending_openapi/tutorial005.py!} |
|||
``` |
|||
|
|||
### Other Swagger UI Parameters |
|||
|
|||
To see all the other possible configurations you can use, read the official <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">docs for Swagger UI parameters</a>. |
|||
|
|||
### JavaScript-only settings |
|||
|
|||
Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). |
|||
|
|||
FastAPI also includes these JavaScript-only `presets` settings: |
|||
|
|||
```JavaScript |
|||
presets: [ |
|||
SwaggerUIBundle.presets.apis, |
|||
SwaggerUIBundle.SwaggerUIStandalonePreset |
|||
] |
|||
``` |
|||
|
|||
These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. |
|||
|
|||
If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. |
@ -0,0 +1,17 @@ |
|||
# Deploy FastAPI on Cloud Providers |
|||
|
|||
You can use virtually **any cloud provider** to deploy your FastAPI application. |
|||
|
|||
In most of the cases, the main cloud providers have guides to deploy FastAPI with them. |
|||
|
|||
## Cloud Providers - Sponsors |
|||
|
|||
Some cloud providers ✨ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, this ensures the continued and healthy **development** of FastAPI and its **ecosystem**. |
|||
|
|||
And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good service** but also want to make sure you have a **good and healthy framework**, FastAPI. 🙇 |
|||
|
|||
You might want to try their services and follow their guides: |
|||
|
|||
* <a href="https://docs.platform.sh/languages/python.html?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" class="external-link" target="_blank">Platform.sh</a> |
|||
* <a href="https://docs.porter.run/language-specific-guides/fastapi" class="external-link" target="_blank">Porter</a> |
|||
* <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> |
@ -1,391 +0,0 @@ |
|||
# Deploy FastAPI on Deta Space |
|||
|
|||
In this section you will learn how to easily deploy a **FastAPI** application on <a href="https://deta.space?ref=fastapi" class="external-link" target="_blank">Deta Space</a>, for free. 🎁 |
|||
|
|||
It will take you about **10 minutes** to deploy an API that you can use. After that, you can optionally release it to anyone. |
|||
|
|||
Let's dive in. |
|||
|
|||
!!! info |
|||
<a href="https://deta.space?ref=fastapi" class="external-link" target="_blank">Deta</a> is a **FastAPI** sponsor. 🎉 |
|||
|
|||
## A simple **FastAPI** app |
|||
|
|||
* To start, create an empty directory with the name of your app, for example `./fastapi-deta/`, and then navigate into it. |
|||
|
|||
```console |
|||
$ mkdir fastapi-deta |
|||
$ cd fastapi-deta |
|||
``` |
|||
|
|||
### FastAPI code |
|||
|
|||
* Create a `main.py` file with: |
|||
|
|||
```Python |
|||
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): |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
### Requirements |
|||
|
|||
Now, in the same directory create a file `requirements.txt` with: |
|||
|
|||
```text |
|||
fastapi |
|||
uvicorn[standard] |
|||
``` |
|||
|
|||
### Directory structure |
|||
|
|||
You will now have a directory `./fastapi-deta/` with two files: |
|||
|
|||
``` |
|||
. |
|||
└── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
## Create a free **Deta Space** account |
|||
|
|||
Next, create a free account on <a href="https://deta.space/signup?dev_mode=true&ref=fastapi" class="external-link" target="_blank">Deta Space</a>, you just need an email and password. |
|||
|
|||
You don't even need a credit card, but make sure **Developer Mode** is enabled when you sign up. |
|||
|
|||
|
|||
## Install the CLI |
|||
|
|||
Once you have your account, install the Deta Space <abbr title="Command Line Interface application">CLI</abbr>: |
|||
|
|||
=== "Linux, macOS" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ curl -fsSL https://get.deta.dev/space-cli.sh | sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ iwr https://get.deta.dev/space-cli.ps1 -useb | iex |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
After installing it, open a new terminal so that the installed CLI is detected. |
|||
|
|||
In a new terminal, confirm that it was correctly installed with: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ space --help |
|||
|
|||
Deta command line interface for managing deta micros. |
|||
Complete documentation available at https://deta.space/docs |
|||
|
|||
Usage: |
|||
space [flags] |
|||
space [command] |
|||
|
|||
Available Commands: |
|||
help Help about any command |
|||
link link code to project |
|||
login login to space |
|||
new create new project |
|||
push push code for project |
|||
release create release for a project |
|||
validate validate spacefile in dir |
|||
version Space CLI version |
|||
... |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip |
|||
If you have problems installing the CLI, check the official <a href="https://deta.space/docs/en/basics/cli?ref=fastapi" class="external-link" target="_blank">Deta Space Documentation</a>. |
|||
|
|||
## Login with the CLI |
|||
|
|||
In order to authenticate your CLI with Deta Space, you will need an access token. |
|||
|
|||
To obtain this token, open your <a href="https://deta.space/login?ref=fastapi" class="external-link" target="_blank">Deta Space Canvas</a>, open the **Teletype** (command bar at the bottom of the Canvas), and then click on **Settings**. From there, select **Generate Token** and copy the resulting token. |
|||
|
|||
<img src="/img/deployment/deta/image03.png"> |
|||
|
|||
Now run `space login` from the Space CLI. Upon pasting the token into the CLI prompt and pressing enter, you should see a confirmation message. |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ space login |
|||
|
|||
To authenticate the Space CLI with your Space account, generate a new access token in your Space settings and paste it below: |
|||
|
|||
# Enter access token (41 chars) >$ ***************************************** |
|||
|
|||
👍 Login Successful! |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## Create a new project in Space |
|||
|
|||
Now that you've authenticated with the Space CLI, use it to create a new <a href="https://deta.space/docs/en/basics/projects" class="external-link" target="_blank">Space Project</a>: |
|||
|
|||
```console |
|||
$ space new |
|||
|
|||
# What is your project's name? >$ fastapi-deta |
|||
``` |
|||
|
|||
The Space CLI will ask you to name the project, we will call ours `fastapi-deta`. |
|||
|
|||
Then, it will try to automatically detect which framework or language you are using, showing you what it finds. In our case it will identify the Python app with the following message, prompting you to confirm: |
|||
|
|||
```console |
|||
⚙️ No Spacefile found, trying to auto-detect configuration ... |
|||
👇 Deta detected the following configuration: |
|||
|
|||
Micros: |
|||
name: fastapi-deta |
|||
L src: . |
|||
L engine: python3.9 |
|||
|
|||
# Do you want to bootstrap "fastapi-deta" with this configuration? (y/n)$ y |
|||
``` |
|||
|
|||
After you confirm, your project will be created in Deta Space inside a special app called <a href="https://deta.space/docs/en/basics/projects#projects-in-builder?ref=fastapi" class="external-link" target="_blank">Builder</a>. Builder is a toolbox that helps you to create and manage your apps in Deta Space. |
|||
|
|||
The CLI will also create a `Spacefile` locally in the `fastapi-deta` directory. The <a href="https://deta.space/docs/en/reference/spacefile?ref=fastapi" class="external-link" target="_blank">Spacefile</a> is a configuration file which tells Deta Space how to run your app. The `Spacefile` for your app will be as follows: |
|||
|
|||
```yaml |
|||
v: 0 |
|||
micros: |
|||
- name: fastapi-deta |
|||
src: . |
|||
engine: python3.9 |
|||
``` |
|||
|
|||
It is a `yaml` file, and you can use it to add features like scheduled tasks or modify how your app functions, which we'll do later. To learn more, read <a href="https://deta.space/docs/en/reference/spacefile" class="external-link" target="_blank">the `Spacefile` documentation</a>. |
|||
|
|||
!!! tip |
|||
The Space CLI will also create a hidden `.space` folder in your local directory to link your local environment with Deta Space. This folder should not be included in your version control and will automatically be added to your `.gitignore` file, if you have initialized a Git repository. |
|||
|
|||
## Define the run command in the Spacefile |
|||
|
|||
The `run` command in the Spacefile tells Space what command should be executed to start your app. In this case it would be `uvicorn main:app`. |
|||
|
|||
```diff |
|||
v: 0 |
|||
micros: |
|||
- name: fastapi-deta |
|||
src: . |
|||
engine: python3.9 |
|||
+ run: uvicorn main:app |
|||
``` |
|||
|
|||
## Deploy to Deta Space |
|||
|
|||
To get your FastAPI live in the cloud, use one more CLI command: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ space push |
|||
|
|||
---> 100% |
|||
|
|||
build complete... created revision: satyr-jvjk |
|||
|
|||
✔ Successfully pushed your code and created a new Revision! |
|||
ℹ Updating your development instance with the latest Revision, it will be available on your Canvas shortly. |
|||
``` |
|||
</div> |
|||
|
|||
This command will package your code, upload all the necessary files to Deta Space, and run a remote build of your app, resulting in a **revision**. Whenever you run `space push` successfully, a live instance of your API is automatically updated with the latest revision. |
|||
|
|||
!!! tip |
|||
You can manage your <a href="https://deta.space/docs/en/basics/revisions#whats-a-revision" class="external-link" target="_blank">revisions</a> by opening your project in the Builder app. The live copy of your API will be visible under the **Develop** tab in Builder. |
|||
|
|||
## Check it |
|||
|
|||
The live instance of your API will also be added automatically to your Canvas (the dashboard) on Deta Space. |
|||
|
|||
<img src="/img/deployment/deta/image04.png"> |
|||
|
|||
Click on the new app called `fastapi-deta`, and it will open your API in a new browser tab on a URL like `https://fastapi-deta-gj7ka8.deta.app/`. |
|||
|
|||
You will get a JSON response from your FastAPI app: |
|||
|
|||
```JSON |
|||
{ |
|||
"Hello": "World" |
|||
} |
|||
``` |
|||
|
|||
And now you can head over to the `/docs` of your API. For this example, it would be `https://fastapi-deta-gj7ka8.deta.app/docs`. |
|||
|
|||
<img src="/img/deployment/deta/image05.png"> |
|||
|
|||
## Enable public access |
|||
|
|||
Deta will handle authentication for your account using cookies. By default, every app or API that you `push` or install to your Space is personal - it's only accessible to you. |
|||
|
|||
But you can also make your API public using the `Spacefile` from earlier. |
|||
|
|||
With a `public_routes` parameter, you can specify which paths of your API should be available to the public. |
|||
|
|||
Set your `public_routes` to `"*"` to open every route of your API to the public: |
|||
|
|||
```yaml |
|||
v: 0 |
|||
micros: |
|||
- name: fastapi-deta |
|||
src: . |
|||
engine: python3.9 |
|||
public_routes: |
|||
- "/*" |
|||
``` |
|||
|
|||
Then run `space push` again to update your live API on Deta Space. |
|||
|
|||
Once it deploys, you can share your URL with anyone and they will be able to access your API. 🚀 |
|||
|
|||
## HTTPS |
|||
|
|||
Congrats! You deployed your FastAPI app to Deta Space! 🎉 🍰 |
|||
|
|||
Also, notice that Deta Space correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your users will have a secure encrypted connection. ✅ 🔒 |
|||
|
|||
## Create a release |
|||
|
|||
Space also allows you to publish your API. When you publish it, anyone else can install their own copy of your API, in their own Deta Space cloud. |
|||
|
|||
To do so, run `space release` in the Space CLI to create an **unlisted release**: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ space release |
|||
|
|||
# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y |
|||
|
|||
~ Creating a Release with the latest Revision |
|||
|
|||
---> 100% |
|||
|
|||
creating release... |
|||
publishing release in edge locations.. |
|||
completed... |
|||
released: fastapi-deta-exp-msbu |
|||
https://deta.space/discovery/r/5kjhgyxewkdmtotx |
|||
|
|||
Lift off -- successfully created a new Release! |
|||
Your Release is available globally on 5 Deta Edges |
|||
Anyone can install their own copy of your app. |
|||
``` |
|||
</div> |
|||
|
|||
This command publishes your revision as a release and gives you a link. Anyone you give this link to can install your API. |
|||
|
|||
|
|||
You can also make your app publicly discoverable by creating a **listed release** with `space release --listed` in the Space CLI: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ space release --listed |
|||
|
|||
# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y |
|||
|
|||
~ Creating a listed Release with the latest Revision ... |
|||
|
|||
creating release... |
|||
publishing release in edge locations.. |
|||
completed... |
|||
released: fastapi-deta-exp-msbu |
|||
https://deta.space/discovery/@user/fastapi-deta |
|||
|
|||
Lift off -- successfully created a new Release! |
|||
Your Release is available globally on 5 Deta Edges |
|||
Anyone can install their own copy of your app. |
|||
Listed on Discovery for others to find! |
|||
``` |
|||
</div> |
|||
|
|||
This will allow anyone to find and install your app via <a href="https://deta.space/discovery?ref=fastapi" class="external-link" target="_blank">Deta Discovery</a>. Read more about <a href="https://deta.space/docs/en/basics/releases?ref=fastapi" class="external-link" target="_blank">releasing your app in the docs</a>. |
|||
|
|||
## Check runtime logs |
|||
|
|||
Deta Space also lets you inspect the logs of every app you build or install. |
|||
|
|||
Add some logging functionality to your app by adding a `print` statement to your `main.py` file. |
|||
|
|||
```py |
|||
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): |
|||
print(item_id) |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
The code within the `read_item` function includes a print statement that will output the `item_id` that is included in the URL. Send a request to your _path operation_ `/items/{item_id}` from the docs UI (which will have a URL like `https://fastapi-deta-gj7ka8.deta.app/docs`), using an ID like `5` as an example. |
|||
|
|||
Now go to your <a href="https://deta.space?ref=fastapi" class="external-link" target="_blank">Space's Canvas</a>. Click on the context menu (`...`) of your live app instance, and then click on **View Logs**. Here you can view your app's logs, sorted by time. |
|||
|
|||
<img src="/img/deployment/deta/image06.png"> |
|||
|
|||
## Learn more |
|||
|
|||
At some point, you will probably want to store some data for your app in a way that persists through time. For that you can use <a href="https://deta.space/docs/en/basics/data#deta-base?ref=fastapi" class="external-link" target="_blank">Deta Base</a> and <a href="https://deta.space/docs/en/basics/data#deta-drive?ref=fastapi" class="external-link" target="_blank">Deta Drive</a>, both of which have a generous **free tier**. |
|||
|
|||
You can also read more in the <a href="https://deta.space/docs/?ref=fastapi" class="external-link" target="_blank">Deta Space Documentation</a>. |
|||
|
|||
!!! tip |
|||
If you have any Deta related questions, comments, or feedback, head to the <a href="https://go.deta.dev/discord" class="external-link" target="_blank">Deta Discord server</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 Space: |
|||
|
|||
- **HTTPS**: Handled by Deta Space, they will give you a subdomain and handle HTTPS automatically. |
|||
- **Running on startup**: Handled by Deta Space, as part of their service. |
|||
- **Restarts**: Handled by Deta Space, as part of their service. |
|||
- **Replication**: Handled by Deta Space, as part of their service. |
|||
- **Authentication**: Handled by Deta Space, as part of their service. |
|||
- **Memory**: Limit predefined by Deta Space, you could contact them to increase it. |
|||
- **Previous steps before starting**: Can be configured using the <a href="https://deta.space/docs/en/reference/spacefile?ref=fastapi" class="external-link" target="_blank">`Spacefile`</a>. |
|||
|
|||
!!! note |
|||
Deta Space is designed to make it easy and free to build cloud applications for yourself. Then you can optionally share them with anyone. |
|||
|
|||
It can simplify several use cases, but at the same time, it doesn't support others, like using external databases (apart from Deta's own NoSQL database system), custom virtual machines, etc. |
|||
|
|||
You can read more details in the <a href="https://deta.space/docs/en/basics/micros?ref=fastapi" class="external-link" target="_blank">Deta Space Documentation</a> to see if it's the right choice for you. |
@ -1,4 +1,4 @@ |
|||
# Async SQL (Relational) Databases |
|||
# Async SQL (Relational) Databases with Encode/Databases |
|||
|
|||
!!! info |
|||
These docs are about to be updated. 🎉 |
@ -0,0 +1,78 @@ |
|||
# Configure Swagger UI |
|||
|
|||
You can configure some extra <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">Swagger UI parameters</a>. |
|||
|
|||
To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. |
|||
|
|||
`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. |
|||
|
|||
FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. |
|||
|
|||
## Disable Syntax Highlighting |
|||
|
|||
For example, you could disable syntax highlighting in Swagger UI. |
|||
|
|||
Without changing the settings, syntax highlighting is enabled by default: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image02.png"> |
|||
|
|||
But you can disable it by setting `syntaxHighlight` to `False`: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/configure_swagger_ui/tutorial001.py!} |
|||
``` |
|||
|
|||
...and then Swagger UI won't show the syntax highlighting anymore: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image03.png"> |
|||
|
|||
## Change the Theme |
|||
|
|||
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/configure_swagger_ui/tutorial002.py!} |
|||
``` |
|||
|
|||
That configuration would change the syntax highlighting color theme: |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image04.png"> |
|||
|
|||
## Change Default Swagger UI Parameters |
|||
|
|||
FastAPI includes some default configuration parameters appropriate for most of the use cases. |
|||
|
|||
It includes these default configurations: |
|||
|
|||
```Python |
|||
{!../../../fastapi/openapi/docs.py[ln:7-13]!} |
|||
``` |
|||
|
|||
You can override any of them by setting a different value in the argument `swagger_ui_parameters`. |
|||
|
|||
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/configure_swagger_ui/tutorial003.py!} |
|||
``` |
|||
|
|||
## Other Swagger UI Parameters |
|||
|
|||
To see all the other possible configurations you can use, read the official <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">docs for Swagger UI parameters</a>. |
|||
|
|||
## JavaScript-only settings |
|||
|
|||
Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). |
|||
|
|||
FastAPI also includes these JavaScript-only `presets` settings: |
|||
|
|||
```JavaScript |
|||
presets: [ |
|||
SwaggerUIBundle.presets.apis, |
|||
SwaggerUIBundle.SwaggerUIStandalonePreset |
|||
] |
|||
``` |
|||
|
|||
These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. |
|||
|
|||
If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. |
@ -0,0 +1,199 @@ |
|||
# Custom Docs UI Static Assets (Self-Hosting) |
|||
|
|||
The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files. |
|||
|
|||
By default, those files are served from a <abbr title="Content Delivery Network: A service, normally composed of several servers, that provides static files, like JavaScript and CSS. It's commonly used to serve those files from the server closer to the client, improving performance.">CDN</abbr>. |
|||
|
|||
But it's possible to customize it, you can set a specific CDN, or serve the files yourself. |
|||
|
|||
## Custom CDN for JavaScript and CSS |
|||
|
|||
Let's say that you want to use a different <abbr title="Content Delivery Network">CDN</abbr>, for example you want to use `https://unpkg.com/`. |
|||
|
|||
This could be useful if for example you live in a country that restricts some URLs. |
|||
|
|||
### Disable the automatic docs |
|||
|
|||
The first step is to disable the automatic docs, as by default, those use the default CDN. |
|||
|
|||
To disable them, set their URLs to `None` when creating your `FastAPI` app: |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/custom_docs_ui/tutorial001.py!} |
|||
``` |
|||
|
|||
### Include the custom docs |
|||
|
|||
Now you can create the *path operations* for the custom docs. |
|||
|
|||
You can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: |
|||
|
|||
* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. |
|||
* `title`: the title of your API. |
|||
* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. |
|||
* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. This is the custom CDN URL. |
|||
* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. This is the custom CDN URL. |
|||
|
|||
And similarly for ReDoc... |
|||
|
|||
```Python hl_lines="2-6 11-19 22-24 27-33" |
|||
{!../../../docs_src/custom_docs_ui/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! tip |
|||
The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. |
|||
|
|||
If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. |
|||
|
|||
Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. |
|||
|
|||
### Create a *path operation* to test it |
|||
|
|||
Now, to be able to test that everything works, create a *path operation*: |
|||
|
|||
```Python hl_lines="36-38" |
|||
{!../../../docs_src/custom_docs_ui/tutorial001.py!} |
|||
``` |
|||
|
|||
### Test it |
|||
|
|||
Now, you should be able to go to your docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, and reload the page, it will load those assets from the new CDN. |
|||
|
|||
## Self-hosting JavaScript and CSS for docs |
|||
|
|||
Self-hosting the JavaScript and CSS could be useful if, for example, you need your app to keep working even while offline, without open Internet access, or in a local network. |
|||
|
|||
Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them. |
|||
|
|||
### Project file structure |
|||
|
|||
Let's say your project file structure looks like this: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
``` |
|||
|
|||
Now create a directory to store those static files. |
|||
|
|||
Your new file structure could look like this: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
└── static/ |
|||
``` |
|||
|
|||
### Download the files |
|||
|
|||
Download the static files needed for the docs and put them on that `static/` directory. |
|||
|
|||
You can probably right-click each link and select an option similar to `Save link as...`. |
|||
|
|||
**Swagger UI** uses the files: |
|||
|
|||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a> |
|||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a> |
|||
|
|||
And **ReDoc** uses the file: |
|||
|
|||
* <a href="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a> |
|||
|
|||
After that, your file structure could look like: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ ├── main.py |
|||
└── static |
|||
├── redoc.standalone.js |
|||
├── swagger-ui-bundle.js |
|||
└── swagger-ui.css |
|||
``` |
|||
|
|||
### Serve the static files |
|||
|
|||
* Import `StaticFiles`. |
|||
* "Mount" a `StaticFiles()` instance in a specific path. |
|||
|
|||
```Python hl_lines="7 11" |
|||
{!../../../docs_src/custom_docs_ui/tutorial002.py!} |
|||
``` |
|||
|
|||
### Test the static files |
|||
|
|||
Start your application and go to <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>. |
|||
|
|||
You should see a very long JavaScript file for **ReDoc**. |
|||
|
|||
It could start with something like: |
|||
|
|||
```JavaScript |
|||
/*! |
|||
* ReDoc - OpenAPI/Swagger-generated API Reference Documentation |
|||
* ------------------------------------------------------------- |
|||
* Version: "2.0.0-rc.18" |
|||
* Repo: https://github.com/Redocly/redoc |
|||
*/ |
|||
!function(e,t){"object"==typeof exports&&"object"==typeof m |
|||
|
|||
... |
|||
``` |
|||
|
|||
That confirms that you are being able to serve static files from your app, and that you placed the static files for the docs in the correct place. |
|||
|
|||
Now we can configure the app to use those static files for the docs. |
|||
|
|||
### Disable the automatic docs for static files |
|||
|
|||
The same as when using a custom CDN, the first step is to disable the automatic docs, as those use the CDN by default. |
|||
|
|||
To disable them, set their URLs to `None` when creating your `FastAPI` app: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/custom_docs_ui/tutorial002.py!} |
|||
``` |
|||
|
|||
### Include the custom docs for static files |
|||
|
|||
And the same way as with a custom CDN, now you can create the *path operations* for the custom docs. |
|||
|
|||
Again, you can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: |
|||
|
|||
* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. |
|||
* `title`: the title of your API. |
|||
* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. |
|||
* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. **This is the one that your own app is now serving**. |
|||
* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. **This is the one that your own app is now serving**. |
|||
|
|||
And similarly for ReDoc... |
|||
|
|||
```Python hl_lines="2-6 14-22 25-27 30-36" |
|||
{!../../../docs_src/custom_docs_ui/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! tip |
|||
The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. |
|||
|
|||
If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. |
|||
|
|||
Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. |
|||
|
|||
### Create a *path operation* to test static files |
|||
|
|||
Now, to be able to test that everything works, create a *path operation*: |
|||
|
|||
```Python hl_lines="39-41" |
|||
{!../../../docs_src/custom_docs_ui/tutorial002.py!} |
|||
``` |
|||
|
|||
### Test Static Files UI |
|||
|
|||
Now, you should be able to disconnect your WiFi, go to your docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, and reload the page. |
|||
|
|||
And even without Internet, you would be able to see the docs for your API and interact with it. |
@ -0,0 +1,87 @@ |
|||
# Extending OpenAPI |
|||
|
|||
There are some cases where you might need to modify the generated OpenAPI schema. |
|||
|
|||
In this section you will see how. |
|||
|
|||
## The normal process |
|||
|
|||
The normal (default) process, is as follows. |
|||
|
|||
A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema. |
|||
|
|||
As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered. |
|||
|
|||
It just returns a JSON response with the result of the application's `.openapi()` method. |
|||
|
|||
By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them. |
|||
|
|||
If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`. |
|||
|
|||
And that function `get_openapi()` receives as parameters: |
|||
|
|||
* `title`: The OpenAPI title, shown in the docs. |
|||
* `version`: The version of your API, e.g. `2.5.0`. |
|||
* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.1.0`. |
|||
* `summary`: A short summary of the API. |
|||
* `description`: The description of your API, this can include markdown and will be shown in the docs. |
|||
* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`. |
|||
|
|||
!!! info |
|||
The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by FastAPI 0.99.0 and above. |
|||
|
|||
## Overriding the defaults |
|||
|
|||
Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need. |
|||
|
|||
For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">ReDoc's OpenAPI extension to include a custom logo</a>. |
|||
|
|||
### Normal **FastAPI** |
|||
|
|||
First, write all your **FastAPI** application as normally: |
|||
|
|||
```Python hl_lines="1 4 7-9" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Generate the OpenAPI schema |
|||
|
|||
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function: |
|||
|
|||
```Python hl_lines="2 15-21" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Modify the OpenAPI schema |
|||
|
|||
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema: |
|||
|
|||
```Python hl_lines="22-24" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Cache the OpenAPI schema |
|||
|
|||
You can use the property `.openapi_schema` as a "cache", to store your generated schema. |
|||
|
|||
That way, your application won't have to generate the schema every time a user opens your API docs. |
|||
|
|||
It will be generated only once, and then the same cached schema will be used for the next requests. |
|||
|
|||
```Python hl_lines="13-14 25-26" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Override the method |
|||
|
|||
Now you can replace the `.openapi()` method with your new function. |
|||
|
|||
```Python hl_lines="29" |
|||
{!../../../docs_src/extending_openapi/tutorial001.py!} |
|||
``` |
|||
|
|||
### Check it |
|||
|
|||
Once you go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> you will see that you are using your custom logo (in this example, **FastAPI**'s logo): |
|||
|
|||
<img src="/img/tutorial/extending-openapi/image01.png"> |
@ -0,0 +1,39 @@ |
|||
# General - How To - Recipes |
|||
|
|||
Here are several pointers to other places in the docs, for general or frequent questions. |
|||
|
|||
## Filter Data - Security |
|||
|
|||
To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}. |
|||
|
|||
## Documentation Tags - OpenAPI |
|||
|
|||
To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}. |
|||
|
|||
## Documentation Summary and Description - OpenAPI |
|||
|
|||
To add a summary and description to your *path operations*, and show them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}. |
|||
|
|||
## Documentation Response description - OpenAPI |
|||
|
|||
To define the description of the response, shown in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}. |
|||
|
|||
## Documentation Deprecate a *Path Operation* - OpenAPI |
|||
|
|||
To deprecate a *path operation*, and show it in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}. |
|||
|
|||
## Convert any Data to JSON-compatible |
|||
|
|||
To convert any data to JSON-compatible, read the docs for [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}. |
|||
|
|||
## OpenAPI Metadata - Docs |
|||
|
|||
To add metadata to your OpenAPI schema, including a license, version, contact, etc, read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md){.internal-link target=_blank}. |
|||
|
|||
## OpenAPI Custom URL |
|||
|
|||
To customize the OpenAPI URL (or remove it), read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}. |
|||
|
|||
## OpenAPI Docs URLs |
|||
|
|||
To update the URLs used for the automatically generated docs user interfaces, read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}. |
@ -0,0 +1,11 @@ |
|||
# How To - Recipes |
|||
|
|||
Here you will see different recipes or "how to" guides for **several topics**. |
|||
|
|||
Most of these ideas would be more or less **independent**, and in most cases you should only need to study them if they apply directly to **your project**. |
|||
|
|||
If something seems interesting and useful to your project, go ahead and check it, but otherwise, you might probably just skip them. |
|||
|
|||
!!! tip |
|||
|
|||
If you want to **learn FastAPI** in a structured way (recommended), go and read the [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} chapter by chapter instead. |
@ -1,4 +1,4 @@ |
|||
# NoSQL (Distributed / Big Data) Databases |
|||
# NoSQL (Distributed / Big Data) Databases with Couchbase |
|||
|
|||
!!! info |
|||
These docs are about to be updated. 🎉 |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 4.7 KiB |
@ -1,245 +0,0 @@ |
|||
# Déployer FastAPI sur Deta |
|||
|
|||
Dans cette section, vous apprendrez à déployer facilement une application **FastAPI** sur <a href="https://www.deta. |
|||
sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> en utilisant le plan tarifaire gratuit. 🎁 |
|||
|
|||
Cela vous prendra environ **10 minutes**. |
|||
|
|||
!!! info |
|||
<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> sponsorise **FastAPI**. 🎉 |
|||
|
|||
## Une application **FastAPI** de base |
|||
|
|||
* Créez un répertoire pour votre application, par exemple `./fastapideta/` et déplacez-vous dedans. |
|||
|
|||
### Le code FastAPI |
|||
|
|||
* Créer un fichier `main.py` avec : |
|||
|
|||
```Python |
|||
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): |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
### Dépendances |
|||
|
|||
Maintenant, dans le même répertoire, créez un fichier `requirements.txt` avec : |
|||
|
|||
```text |
|||
fastapi |
|||
``` |
|||
|
|||
!!! tip "Astuce" |
|||
Il n'est pas nécessaire d'installer Uvicorn pour déployer sur Deta, bien qu'il soit probablement souhaitable de l'installer localement pour tester votre application. |
|||
|
|||
### Structure du répertoire |
|||
|
|||
Vous aurez maintenant un répertoire `./fastapideta/` avec deux fichiers : |
|||
|
|||
``` |
|||
. |
|||
└── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
## Créer un compte gratuit sur Deta |
|||
|
|||
Créez maintenant un <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">compte gratuit |
|||
sur Deta</a>, vous avez juste besoin d'une adresse email et d'un mot de passe. |
|||
|
|||
Vous n'avez même pas besoin d'une carte de crédit. |
|||
|
|||
## Installer le CLI (Interface en Ligne de Commande) |
|||
|
|||
Une fois que vous avez votre compte, installez le <abbr title="Command Line Interface application">CLI</abbr> de Deta : |
|||
|
|||
=== "Linux, macOS" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ curl -fsSL https://get.deta.dev/cli.sh | sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ iwr https://get.deta.dev/cli.ps1 -useb | iex |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Après l'avoir installé, ouvrez un nouveau terminal afin que la nouvelle installation soit détectée. |
|||
|
|||
Dans un nouveau terminal, confirmez qu'il a été correctement installé avec : |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta --help |
|||
|
|||
Deta command line interface for managing deta micros. |
|||
Complete documentation available at https://docs.deta.sh |
|||
|
|||
Usage: |
|||
deta [flags] |
|||
deta [command] |
|||
|
|||
Available Commands: |
|||
auth Change auth settings for a deta micro |
|||
|
|||
... |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Astuce" |
|||
Si vous rencontrez des problèmes pour installer le CLI, consultez la <a href="https://docs.deta. sh/docs/micros/getting_started?ref=fastapi" class="external-link" target="_blank">documentation officielle de Deta (en anglais)</a>. |
|||
|
|||
## Connexion avec le CLI |
|||
|
|||
Maintenant, connectez-vous à Deta depuis le CLI avec : |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta login |
|||
|
|||
Please, log in from the web page. Waiting.. |
|||
Logged in successfully. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Cela ouvrira un navigateur web et permettra une authentification automatique. |
|||
|
|||
## Déployer avec Deta |
|||
|
|||
Ensuite, déployez votre application avec le CLI de Deta : |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta new |
|||
|
|||
Successfully created a new micro |
|||
|
|||
// Notice the "endpoint" 🔍 |
|||
|
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
|
|||
Adding dependencies... |
|||
|
|||
|
|||
---> 100% |
|||
|
|||
|
|||
Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Vous verrez un message JSON similaire à : |
|||
|
|||
```JSON hl_lines="4" |
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "Astuce" |
|||
Votre déploiement aura une URL `"endpoint"` différente. |
|||
|
|||
## Vérifiez |
|||
|
|||
Maintenant, dans votre navigateur ouvrez votre URL `endpoint`. Dans l'exemple ci-dessus, c'était |
|||
`https://qltnci.deta.dev`, mais la vôtre sera différente. |
|||
|
|||
Vous verrez la réponse JSON de votre application FastAPI : |
|||
|
|||
```JSON |
|||
{ |
|||
"Hello": "World" |
|||
} |
|||
``` |
|||
|
|||
Et maintenant naviguez vers `/docs` dans votre API, dans l'exemple ci-dessus ce serait `https://qltnci.deta.dev/docs`. |
|||
|
|||
Vous verrez votre documentation comme suit : |
|||
|
|||
<img src="/img/deployment/deta/image01.png"> |
|||
|
|||
## Activer l'accès public |
|||
|
|||
Par défaut, Deta va gérer l'authentification en utilisant des cookies pour votre compte. |
|||
|
|||
Mais une fois que vous êtes prêt, vous pouvez le rendre public avec : |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta auth disable |
|||
|
|||
Successfully disabled http auth |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Maintenant, vous pouvez partager cette URL avec n'importe qui et ils seront en mesure d'accéder à votre API. 🚀 |
|||
|
|||
## HTTPS |
|||
|
|||
Félicitations ! Vous avez déployé votre application FastAPI sur Deta ! 🎉 🍰 |
|||
|
|||
Remarquez également que Deta gère correctement HTTPS pour vous, vous n'avez donc pas à vous en occuper et pouvez être sûr que vos clients auront une connexion cryptée sécurisée. ✅ 🔒 |
|||
|
|||
## Vérifiez le Visor |
|||
|
|||
À partir de l'interface graphique de votre documentation (dans une URL telle que `https://qltnci.deta.dev/docs`) |
|||
envoyez une requête à votre *opération de chemin* `/items/{item_id}`. |
|||
|
|||
Par exemple avec l'ID `5`. |
|||
|
|||
Allez maintenant sur <a href="https://web.deta.sh/" class="external-link" target="_blank">https://web.deta.sh</a>. |
|||
|
|||
Vous verrez qu'il y a une section à gauche appelée <abbr title="ça vient de Micro(server)">"Micros"</abbr> avec chacune de vos applications. |
|||
|
|||
Vous verrez un onglet avec "Details", et aussi un onglet "Visor", allez à l'onglet "Visor". |
|||
|
|||
Vous pouvez y consulter les requêtes récentes envoyées à votre application. |
|||
|
|||
Vous pouvez également les modifier et les relancer. |
|||
|
|||
<img src="/img/deployment/deta/image02.png"> |
|||
|
|||
## En savoir plus |
|||
|
|||
À un moment donné, vous voudrez probablement stocker certaines données pour votre application d'une manière qui |
|||
persiste dans le temps. Pour cela, vous pouvez utiliser <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a>, il dispose également d'un généreux **plan gratuit**. |
|||
|
|||
Vous pouvez également en lire plus dans la <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">documentation Deta</a>. |
@ -0,0 +1,323 @@ |
|||
# デプロイメントのコンセプト |
|||
|
|||
**FastAPI**を用いたアプリケーションをデプロイするとき、もしくはどのようなタイプのWeb APIであっても、おそらく気になるコンセプトがいくつかあります。 |
|||
|
|||
それらを活用することでアプリケーションを**デプロイするための最適な方法**を見つけることができます。 |
|||
|
|||
重要なコンセプトのいくつかを紹介します: |
|||
|
|||
* セキュリティ - HTTPS |
|||
* 起動時の実行 |
|||
* 再起動 |
|||
* レプリケーション(実行中のプロセス数) |
|||
* メモリー |
|||
* 開始前の事前のステップ |
|||
|
|||
これらが**デプロイメント**にどのような影響を与えるかを見ていきましょう。 |
|||
|
|||
最終的な目的は、**安全な方法で**APIクライアントに**サービスを提供**し、**中断を回避**するだけでなく、**計算リソース**(例えばリモートサーバー/仮想マシン)を可能な限り効率的に使用することです。🚀 |
|||
|
|||
この章では前述した**コンセプト**についてそれぞれ説明します。 |
|||
|
|||
この説明を通して、普段とは非常に異なる環境や存在しないであろう**将来の**環境に対し、デプロイの方法を決める上で必要な**直感**を与えてくれることを願っています。 |
|||
|
|||
これらのコンセプトを意識することにより、**あなた自身のAPI**をデプロイするための最適な方法を**評価**し、**設計**することができるようになるでしょう。 |
|||
|
|||
次の章では、FastAPIアプリケーションをデプロイするための**具体的なレシピ**を紹介します。 |
|||
|
|||
しかし、今はこれらの重要な**コンセプトに基づくアイデア**を確認しましょう。これらのコンセプトは、他のどのタイプのWeb APIにも当てはまります。💡 |
|||
|
|||
## セキュリティ - HTTPS |
|||
|
|||
<!-- NOTE: https.md written in Japanese does not exist, so it redirects to English one --> |
|||
[前チャプターのHTTPSについて](./https.md){.internal-link target=_blank}では、HTTPSがどのようにAPIを暗号化するのかについて学びました。 |
|||
|
|||
通常、アプリケーションサーバにとって**外部の**コンポーネントである**TLS Termination Proxy**によって提供されることが一般的です。このプロキシは通信の暗号化を担当します。 |
|||
|
|||
さらにセキュアな通信において、HTTPS証明書の定期的な更新を行いますが、これはTLS Termination Proxyと同じコンポーネントが担当することもあれば、別のコンポーネントが担当することもあります。 |
|||
|
|||
### HTTPS 用ツールの例 |
|||
TLS Termination Proxyとして使用できるツールには以下のようなものがあります: |
|||
|
|||
* Traefik |
|||
* 証明書の更新を自動的に処理 ✨ |
|||
* Caddy |
|||
* 証明書の更新を自動的に処理 ✨ |
|||
* Nginx |
|||
* 証明書更新のためにCertbotのような外部コンポーネントを使用 |
|||
* HAProxy |
|||
* 証明書更新のためにCertbotのような外部コンポーネントを使用 |
|||
* Nginx のような Ingress Controller を持つ Kubernetes |
|||
* 証明書の更新に cert-manager のような外部コンポーネントを使用 |
|||
* クラウド・プロバイダーがサービスの一部として内部的に処理(下記を参照👇) |
|||
|
|||
もう1つの選択肢は、HTTPSのセットアップを含んだより多くの作業を行う**クラウド・サービス**を利用することです。 このサービスには制限があったり、料金が高くなったりする可能性があります。しかしその場合、TLS Termination Proxyを自分でセットアップする必要はないです。 |
|||
|
|||
次の章で具体例をいくつか紹介します。 |
|||
|
|||
--- |
|||
|
|||
次に考慮すべきコンセプトは、実際のAPIを実行するプログラム(例:Uvicorn)に関連するものすべてです。 |
|||
|
|||
## プログラム と プロセス |
|||
|
|||
私たちは「**プロセス**」という言葉についてたくさん話すので、その意味や「**プログラム**」という言葉との違いを明確にしておくと便利です。 |
|||
|
|||
### プログラムとは何か |
|||
|
|||
**プログラム**という言葉は、一般的にいろいろなものを表現するのに使われます: |
|||
|
|||
* プログラマが書く**コード**、**Pythonファイル** |
|||
* OSによって実行することができるファイル(例: `python`, `python.exe` or `uvicorn`) |
|||
* OS上で**実行**している間、CPUを使用し、メモリ上に何かを保存する特定のプログラム(**プロセス**とも呼ばれる) |
|||
|
|||
### プロセスとは何か |
|||
|
|||
**プロセス**という言葉は通常、より具体的な意味で使われ、OSで実行されているものだけを指します(先ほどの最後の説明のように): |
|||
|
|||
* OS上で**実行**している特定のプログラム |
|||
* これはファイルやコードを指すのではなく、OSによって**実行**され、管理されているものを指します。 |
|||
* どんなプログラムやコードも、それが**実行されているときにだけ機能**します。つまり、**プロセスとして実行されているときだけ**です。 |
|||
* プロセスは、ユーザーにあるいはOSによって、 **終了**(あるいは "kill")させることができます。その時点で、プロセスは実行/実行されることを停止し、それ以降は**何もできなくなります**。 |
|||
* コンピュータで実行されている各アプリケーションは、実行中のプログラムや各ウィンドウなど、その背後にいくつかのプロセスを持っています。そして通常、コンピュータが起動している間、**多くのプロセスが**同時に実行されています。 |
|||
* **同じプログラム**の**複数のプロセス**が同時に実行されていることがあります。 |
|||
|
|||
OSの「タスク・マネージャー」や「システム・モニター」(または同様のツール)を確認すれば、これらのプロセスの多くが実行されているの見ることができるでしょう。 |
|||
|
|||
例えば、同じブラウザプログラム(Firefox、Chrome、Edgeなど)を実行しているプロセスが複数あることがわかります。通常、1つのタブにつき1つのプロセスが実行され、さらに他のプロセスも実行されます。 |
|||
|
|||
<img class="shadow" src="/img/deployment/concepts/image01.png"> |
|||
|
|||
--- |
|||
|
|||
さて、**プロセス**と**プログラム**という用語の違いを確認したところで、デプロイメントについて話を続けます。 |
|||
|
|||
## 起動時の実行 |
|||
|
|||
ほとんどの場合、Web APIを作成するときは、クライアントがいつでもアクセスできるように、**常に**中断されることなく**実行される**ことを望みます。もちろん、特定の状況でのみ実行させたい特別な理由がある場合は別ですが、その時間のほとんどは、常に実行され、**利用可能**であることを望みます。 |
|||
|
|||
### リモートサーバー上での実行 |
|||
|
|||
リモートサーバー(クラウドサーバー、仮想マシンなど)をセットアップするときにできる最も簡単なことは、ローカルで開発するときと同じように、Uvicorn(または同様のもの)を手動で実行することです。 この方法は**開発中**には役に立つと思われます。 |
|||
|
|||
しかし、サーバーへの接続が切れた場合、**実行中のプロセス**はおそらくダウンしてしまうでしょう。 |
|||
|
|||
そしてサーバーが再起動された場合(アップデートやクラウドプロバイダーからのマイグレーションの後など)、おそらくあなたはそれに**気づかないでしょう**。そのため、プロセスを手動で再起動しなければならないことすら気づかないでしょう。つまり、APIはダウンしたままなのです。😱 |
|||
|
|||
### 起動時に自動的に実行 |
|||
|
|||
一般的に、サーバープログラム(Uvicornなど)はサーバー起動時に自動的に開始され、**人の介入**を必要とせずに、APIと一緒にプロセスが常に実行されるようにしたいと思われます(UvicornがFastAPIアプリを実行するなど)。 |
|||
|
|||
### 別のプログラムの用意 |
|||
|
|||
これを実現するために、通常は**別のプログラム**を用意し、起動時にアプリケーションが実行されるようにします。そして多くの場合、他のコンポーネントやアプリケーション、例えばデータベースも実行されるようにします。 |
|||
|
|||
### 起動時に実行するツールの例 |
|||
|
|||
実行するツールの例をいくつか挙げます: |
|||
|
|||
* Docker |
|||
* Kubernetes |
|||
* Docker Compose |
|||
* Swarm モードによる Docker |
|||
* Systemd |
|||
* Supervisor |
|||
* クラウドプロバイダーがサービスの一部として内部的に処理 |
|||
* そのほか... |
|||
|
|||
次の章で、より具体的な例を挙げていきます。 |
|||
|
|||
## 再起動 |
|||
|
|||
起動時にアプリケーションが実行されることを確認するのと同様に、失敗後にアプリケーションが**再起動**されることも確認したいと思われます。 |
|||
|
|||
### 我々は間違いを犯す |
|||
|
|||
私たち人間は常に**間違い**を犯します。ソフトウェアには、ほとんど常に**バグ**があらゆる箇所に隠されています。🐛 |
|||
|
|||
### 小さなエラーは自動的に処理される |
|||
|
|||
FastAPIでWeb APIを構築する際に、コードにエラーがある場合、FastAPIは通常、エラーを引き起こした単一のリクエストにエラーを含めます。🛡 |
|||
|
|||
クライアントはそのリクエストに対して**500 Internal Server Error**を受け取りますが、アプリケーションは完全にクラッシュするのではなく、次のリクエストのために動作を続けます。 |
|||
|
|||
### 重大なエラー - クラッシュ |
|||
|
|||
しかしながら、**アプリケーション全体をクラッシュさせるようなコードを書いて**UvicornとPythonをクラッシュさせるようなケースもあるかもしれません。💥 |
|||
|
|||
それでも、ある箇所でエラーが発生したからといって、アプリケーションを停止させたままにしたくないでしょう。 少なくとも壊れていない*パスオペレーション*については、**実行し続けたい**はずです。 |
|||
|
|||
### クラッシュ後の再起動 |
|||
|
|||
しかし、実行中の**プロセス**をクラッシュさせるような本当にひどいエラーの場合、少なくとも2〜3回ほどプロセスを**再起動**させる外部コンポーネントが必要でしょう。 |
|||
|
|||
!!! tip |
|||
...とはいえ、アプリケーション全体が**すぐにクラッシュする**のであれば、いつまでも再起動し続けるのは意味がないでしょう。しかし、その場合はおそらく開発中か少なくともデプロイ直後に気づくと思われます。 |
|||
|
|||
そこで、**将来**クラッシュする可能性があり、それでも再スタートさせることに意味があるような、主なケースに焦点を当ててみます。 |
|||
|
|||
あなたはおそらく**外部コンポーネント**がアプリケーションの再起動を担当することを望むと考えます。 なぜなら、その時点でUvicornとPythonを使った同じアプリケーションはすでにクラッシュしており、同じアプリケーションの同じコードに対して何もできないためです。 |
|||
|
|||
### 自動的に再起動するツールの例 |
|||
|
|||
ほとんどの場合、前述した**起動時にプログラムを実行する**ために使用されるツールは、自動で**再起動**することにも利用されます。 |
|||
|
|||
例えば、次のようなものがあります: |
|||
|
|||
* Docker |
|||
* Kubernetes |
|||
* Docker Compose |
|||
* Swarm モードによる Docker |
|||
* Systemd |
|||
* Supervisor |
|||
* クラウドプロバイダーがサービスの一部として内部的に処理 |
|||
* そのほか... |
|||
|
|||
## レプリケーション - プロセスとメモリー |
|||
|
|||
FastAPI アプリケーションでは、Uvicorn のようなサーバープログラムを使用し、**1つのプロセス**で1度に複数のクライアントに同時に対応できます。 |
|||
|
|||
しかし、多くの場合、複数のワーカー・プロセスを同時に実行したいと考えるでしょう。 |
|||
|
|||
### 複数のプロセス - Worker |
|||
|
|||
クライアントの数が単一のプロセスで処理できる数を超えており(たとえば仮想マシンがそれほど大きくない場合)、かつサーバーの CPU に**複数のコア**がある場合、同じアプリケーションで同時に**複数のプロセス**を実行させ、すべてのリクエストを分散させることができます。 |
|||
|
|||
同じAPIプログラムの**複数のプロセス**を実行する場合、それらは一般的に**Worker/ワーカー**と呼ばれます。 |
|||
|
|||
### ワーカー・プロセス と ポート |
|||
<!-- NOTE: https.md written in Japanese does not exist, so it redirects to English one --> |
|||
|
|||
[HTTPSについて](./https.md){.internal-link target=_blank}のドキュメントで、1つのサーバーで1つのポートとIPアドレスの組み合わせでリッスンできるのは1つのプロセスだけであることを覚えていますでしょうか? |
|||
|
|||
これはいまだに同じです。 |
|||
|
|||
そのため、**複数のプロセス**を同時に持つには**ポートでリッスンしている単一のプロセス**が必要であり、それが何らかの方法で各ワーカー・プロセスに通信を送信することが求められます。 |
|||
|
|||
### プロセスあたりのメモリー |
|||
|
|||
さて、プログラムがメモリにロードする際には、例えば機械学習モデルや大きなファイルの内容を変数に入れたりする場合では、**サーバーのメモリ(RAM)**を少し消費します。 |
|||
|
|||
そして複数のプロセスは通常、**メモリを共有しません**。これは、実行中の各プロセスがそれぞれ独自の変数やメモリ等を持っていることを意味します。つまり、コード内で大量のメモリを消費している場合、**各プロセス**は同等の量のメモリを消費することになります。 |
|||
|
|||
### サーバーメモリー |
|||
|
|||
例えば、あなたのコードが **1GBのサイズの機械学習モデル**をロードする場合、APIで1つのプロセスを実行すると、少なくとも1GBのRAMを消費します。 |
|||
|
|||
また、**4つのプロセス**(4つのワーカー)を起動すると、それぞれが1GBのRAMを消費します。つまり、合計でAPIは**4GBのRAM**を消費することになります。 |
|||
|
|||
リモートサーバーや仮想マシンのRAMが3GBしかない場合、4GB以上のRAMをロードしようとすると問題が発生します。🚨 |
|||
|
|||
### 複数プロセス - 例 |
|||
|
|||
この例では、2つの**ワーカー・プロセス**を起動し制御する**マネージャー・ プロセス**があります。 |
|||
|
|||
このマネージャー・ プロセスは、おそらくIPの**ポート**でリッスンしているものです。そして、すべての通信をワーカー・プロセスに転送します。 |
|||
|
|||
これらのワーカー・プロセスは、アプリケーションを実行するものであり、**リクエスト**を受けて**レスポンス**を返すための主要な計算を行い、あなたが変数に入れたものは何でもRAMにロードします。 |
|||
|
|||
<img src="/img/deployment/concepts/process-ram.svg"> |
|||
|
|||
そしてもちろん、同じマシンでは、あなたのアプリケーションとは別に、**他のプロセス**も実行されているでしょう。 |
|||
|
|||
興味深いことに、各プロセスが使用する**CPU**の割合は時間とともに大きく**変動**する可能性がありますが、**メモリ(RAM)**は通常、多かれ少なかれ**安定**します。 |
|||
|
|||
毎回同程度の計算を行うAPIがあり、多くのクライアントがいるのであれば、**CPU使用率**もおそらく**安定**するでしょう(常に急激に上下するのではなく)。 |
|||
|
|||
### レプリケーション・ツールと戦略の例 |
|||
|
|||
これを実現するにはいくつかのアプローチがありますが、具体的な戦略については次の章(Dockerやコンテナの章など)で詳しく説明します。 |
|||
|
|||
考慮すべき主な制約は、**パブリックIP**の**ポート**を処理する**単一の**コンポーネントが存在しなければならないということです。 |
|||
|
|||
そして、レプリケートされた**プロセス/ワーカー**に通信を**送信**する方法を持つ必要があります。 |
|||
|
|||
考えられる組み合わせと戦略をいくつか紹介します: |
|||
|
|||
* **Gunicorn**が**Uvicornワーカー**を管理 |
|||
* Gunicornは**IP**と**ポート**をリッスンする**プロセスマネージャ**で、レプリケーションは**複数のUvicornワーカー・プロセス**を持つことによって行われる。 |
|||
* **Uvicorn**が**Uvicornワーカー**を管理 |
|||
* 1つのUvicornの**プロセスマネージャー**が**IP**と**ポート**をリッスンし、**複数のUvicornワーカー・プロセス**を起動する。 |
|||
* **Kubernetes**やその他の分散**コンテナ・システム** |
|||
* **Kubernetes**レイヤーの何かが**IP**と**ポート**をリッスンする。レプリケーションは、**複数のコンテナ**にそれぞれ**1つのUvicornプロセス**を実行させることで行われる。 |
|||
* **クラウド・サービス**によるレプリケーション |
|||
* クラウド・サービスはおそらく**あなたのためにレプリケーションを処理**します。**実行するプロセス**や使用する**コンテナイメージ**を定義できるかもしれませんが、いずれにせよ、それはおそらく**単一のUvicornプロセス**であり、クラウドサービスはそのレプリケーションを担当するでしょう。 |
|||
|
|||
!!! tip |
|||
これらの**コンテナ**やDockerそしてKubernetesに関する項目が、まだあまり意味をなしていなくても心配しないでください。 |
|||
<!-- NOTE: the current version of docker.md is outdated compared to English one. --> |
|||
|
|||
コンテナ・イメージ、Docker、Kubernetesなどについては、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](./docker.md){.internal-link target=_blank}. |
|||
|
|||
## 開始前の事前のステップ |
|||
|
|||
アプリケーションを**開始する前**に、いくつかのステップを実行したい場合が多くあります。 |
|||
|
|||
例えば、**データベース・マイグレーション** を実行したいかもしれません。 |
|||
|
|||
しかしほとんどの場合、これらの手順を**1度**に実行したいと考えるでしょう。 |
|||
|
|||
そのため、アプリケーションを開始する前の**事前のステップ**を実行する**単一のプロセス**を用意したいと思われます。 |
|||
|
|||
そして、それらの事前のステップを実行しているのが単一のプロセスであることを確認する必要があります。このことはその後アプリケーション自体のために**複数のプロセス**(複数のワーカー)を起動した場合も同様です。 |
|||
|
|||
これらのステップが**複数のプロセス**によって実行された場合、**並列**に実行されることによって作業が**重複**することになります。そして、もしそのステップがデータベースのマイグレーションのような繊細なものであった場合、互いに競合を引き起こす可能性があります。 |
|||
|
|||
もちろん、事前のステップを何度も実行しても問題がない場合もあり、その際は対処がかなり楽になります。 |
|||
|
|||
!!! tip |
|||
また、セットアップによっては、アプリケーションを開始する前の**事前のステップ**が必要ない場合もあることを覚えておいてください。 |
|||
|
|||
その場合は、このようなことを心配する必要はないです。🤷 |
|||
|
|||
### 事前ステップの戦略例 |
|||
|
|||
これは**システムを**デプロイする方法に**大きく依存**するだろうし、おそらくプログラムの起動方法や再起動の処理などにも関係してくるでしょう。 |
|||
|
|||
考えられるアイデアをいくつか挙げてみます: |
|||
|
|||
* アプリコンテナの前に実行されるKubernetesのInitコンテナ |
|||
* 事前のステップを実行し、アプリケーションを起動するbashスクリプト |
|||
* 利用するbashスクリプトを起動/再起動したり、エラーを検出したりする方法は以前として必要になるでしょう。 |
|||
|
|||
!!! tip |
|||
<!-- NOTE: the current version of docker.md is outdated compared to English one. --> |
|||
コンテナを使った具体的な例については、次の章で紹介します: [コンテナ内のFastAPI - Docker](./docker.md){.internal-link target=_blank}. |
|||
|
|||
## リソースの利用 |
|||
|
|||
あなたのサーバーは**リソース**であり、プログラムを実行しCPUの計算時間や利用可能なRAMメモリを消費または**利用**することができます。 |
|||
|
|||
システムリソースをどれくらい消費/利用したいですか? 「少ない方が良い」と考えるのは簡単かもしれないですが、実際には、**クラッシュせずに可能な限り**最大限に活用したいでしょう。 |
|||
|
|||
3台のサーバーにお金を払っているにも関わらず、そのRAMとCPUを少ししか使っていないとしたら、おそらく**お金を無駄にしている** 💸、おそらく**サーバーの電力を無駄にしている** 🌎ことになるでしょう。 |
|||
|
|||
その場合は、サーバーを2台だけにして、そのリソース(CPU、メモリ、ディスク、ネットワーク帯域幅など)をより高い割合で使用する方がよいでしょう。 |
|||
|
|||
一方、2台のサーバーがあり、そのCPUとRAMの**100%を使用している**場合、ある時点で1つのプロセスがより多くのメモリを要求し、サーバーはディスクを「メモリ」として使用しないといけません。(何千倍も遅くなる可能性があります。) |
|||
もしくは**クラッシュ**することもあれば、あるいはあるプロセスが何らかの計算をする必要があり、そしてCPUが再び空くまで待たなければならないかもしれません。 |
|||
|
|||
この場合、**1つ余分なサーバー**を用意し、その上でいくつかのプロセスを実行し、すべてのサーバーが**十分なRAMとCPU時間を持つようにする**のがよいでしょう。 |
|||
|
|||
また、何らかの理由でAPIの利用が急増する可能性もあります。もしかしたらそれが流行ったのかもしれないし、他のサービスやボットが使い始めたのかもしれないです。そのような場合に備えて、余分なリソースを用意しておくと安心でしょう。 |
|||
|
|||
例えば、リソース使用率の**50%から90%の範囲**で**任意の数字**をターゲットとすることができます。 |
|||
|
|||
重要なのは、デプロイメントを微調整するためにターゲットを設定し測定することが、おそらく使用したい主要な要素であることです。 |
|||
|
|||
`htop`のような単純なツールを使って、サーバーで使用されているCPUやRAM、あるいは各プロセスで使用されている量を見ることができます。あるいは、より複雑な監視ツールを使って、サーバに分散して使用することもできます。 |
|||
|
|||
## まとめ |
|||
|
|||
アプリケーションのデプロイ方法を決定する際に、考慮すべきであろう主要なコンセプトのいくつかを紹介していきました: |
|||
|
|||
* セキュリティ - HTTPS |
|||
* 起動時の実行 |
|||
* 再起動 |
|||
* レプリケーション(実行中のプロセス数) |
|||
* メモリー |
|||
* 開始前の事前ステップ |
|||
|
|||
これらの考え方とその適用方法を理解することで、デプロイメントを設定したり調整したりする際に必要な直感的な判断ができるようになるはずです。🤓 |
|||
|
|||
次のセクションでは、あなたが取り得る戦略について、より具体的な例を挙げます。🚀 |
@ -1,240 +0,0 @@ |
|||
# Deta にデプロイ |
|||
|
|||
このセクションでは、**FastAPI** アプリケーションを <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> の無料プランを利用して、簡単にデプロイする方法を学習します。🎁 |
|||
|
|||
所要時間は約**10分**です。 |
|||
|
|||
!!! info "備考" |
|||
<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> は **FastAPI** のスポンサーです。🎉 |
|||
|
|||
## ベーシックな **FastAPI** アプリ |
|||
|
|||
* アプリのためのディレクトリ (例えば `./fastapideta/`) を作成し、その中に入ってください。 |
|||
|
|||
### FastAPI のコード |
|||
|
|||
* 以下の `main.py` ファイルを作成してください: |
|||
|
|||
```Python |
|||
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): |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
### Requirements |
|||
|
|||
では、同じディレクトリに以下の `requirements.txt` ファイルを作成してください: |
|||
|
|||
```text |
|||
fastapi |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
アプリのローカルテストのために Uvicorn をインストールしたくなるかもしれませんが、Deta へのデプロイには不要です。 |
|||
|
|||
### ディレクトリ構造 |
|||
|
|||
以下の2つのファイルと1つの `./fastapideta/` ディレクトリがあるはずです: |
|||
|
|||
``` |
|||
. |
|||
└── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
## Detaの無料アカウントの作成 |
|||
|
|||
それでは、<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Detaの無料アカウント</a>を作成しましょう。必要なものはメールアドレスとパスワードだけです。 |
|||
|
|||
クレジットカードさえ必要ありません。 |
|||
|
|||
## CLIのインストール |
|||
|
|||
アカウントを取得したら、Deta <abbr title="Command Line Interface application">CLI</abbr> をインストールしてください: |
|||
|
|||
=== "Linux, macOS" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ curl -fsSL https://get.deta.dev/cli.sh | sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ iwr https://get.deta.dev/cli.ps1 -useb | iex |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
インストールしたら、インストールした CLI を有効にするために新たなターミナルを開いてください。 |
|||
|
|||
新たなターミナル上で、正しくインストールされたか確認します: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta --help |
|||
|
|||
Deta command line interface for managing deta micros. |
|||
Complete documentation available at https://docs.deta.sh |
|||
|
|||
Usage: |
|||
deta [flags] |
|||
deta [command] |
|||
|
|||
Available Commands: |
|||
auth Change auth settings for a deta micro |
|||
|
|||
... |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "豆知識" |
|||
CLI のインストールに問題が発生した場合は、<a href="https://docs.deta.sh/docs/micros/getting_started?ref=fastapi" class="external-link" target="_blank">Deta 公式ドキュメント</a>を参照してください。 |
|||
|
|||
## CLIでログイン |
|||
|
|||
CLI から Deta にログインしてみましょう: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta login |
|||
|
|||
Please, log in from the web page. Waiting.. |
|||
Logged in successfully. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
自動的にウェブブラウザが開いて、認証処理が行われます。 |
|||
|
|||
## Deta でデプロイ |
|||
|
|||
次に、アプリケーションを Deta CLIでデプロイしましょう: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta new |
|||
|
|||
Successfully created a new micro |
|||
|
|||
// Notice the "endpoint" 🔍 |
|||
|
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
|
|||
Adding dependencies... |
|||
|
|||
|
|||
---> 100% |
|||
|
|||
|
|||
Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
次のようなJSONメッセージが表示されます: |
|||
|
|||
```JSON hl_lines="4" |
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
あなたのデプロイでは異なる `"endpoint"` URLが表示されるでしょう。 |
|||
|
|||
## 確認 |
|||
|
|||
それでは、`endpoint` URLをブラウザで開いてみましょう。上記の例では `https://qltnci.deta.dev` ですが、あなたのURLは異なるはずです。 |
|||
|
|||
FastAPIアプリから返ってきたJSONレスポンスが表示されます: |
|||
|
|||
```JSON |
|||
{ |
|||
"Hello": "World" |
|||
} |
|||
``` |
|||
|
|||
そして `/docs` へ移動してください。上記の例では、`https://qltnci.deta.dev/docs` です。 |
|||
|
|||
次のようなドキュメントが表示されます: |
|||
|
|||
<img src="/img/deployment/deta/image01.png"> |
|||
|
|||
## パブリックアクセスの有効化 |
|||
|
|||
デフォルトでは、Deta はクッキーを用いてアカウントの認証を行います。 |
|||
|
|||
しかし、準備が整えば、以下の様に公開できます: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta auth disable |
|||
|
|||
Successfully disabled http auth |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
ここで、URLを共有するとAPIにアクセスできるようになります。🚀 |
|||
|
|||
## HTTPS |
|||
|
|||
おめでとうございます!あなたの FastAPI アプリが Deta へデプロイされました!🎉 🍰 |
|||
|
|||
また、DetaがHTTPSを正しく処理するため、その処理を行う必要がなく、クライアントは暗号化された安全な通信が利用できます。✅ 🔒 |
|||
|
|||
## Visor を確認 |
|||
|
|||
ドキュメントUI (`https://qltnci.deta.dev/docs` のようなURLにある) は *path operation* `/items/{item_id}` へリクエストを送ることができます。 |
|||
|
|||
ID `5` の例を示します。 |
|||
|
|||
まず、<a href="https://web.deta.sh/" class="external-link" target="_blank">https://web.deta.sh</a> へアクセスします。 |
|||
|
|||
左側に各アプリの <abbr title="it comes from Micro(server)">「Micros」</abbr> というセクションが表示されます。 |
|||
|
|||
また、「Details」や「Visor」タブが表示されています。「Visor」タブへ移動してください。 |
|||
|
|||
そこでアプリに送られた直近のリクエストが調べられます。 |
|||
|
|||
また、それらを編集してリプレイできます。 |
|||
|
|||
<img src="/img/deployment/deta/image02.png"> |
|||
|
|||
## さらに詳しく知る |
|||
|
|||
様々な箇所で永続的にデータを保存したくなるでしょう。そのためには <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a> を使用できます。惜しみない **無料利用枠** もあります。 |
|||
|
|||
詳しくは <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">Deta ドキュメント</a>を参照してください。 |
@ -0,0 +1,182 @@ |
|||
# Server Workers - Gunicorn と Uvicorn |
|||
|
|||
前回のデプロイメントのコンセプトを振り返ってみましょう: |
|||
|
|||
* セキュリティ - HTTPS |
|||
* 起動時の実行 |
|||
* 再起動 |
|||
* **レプリケーション(実行中のプロセス数)** |
|||
* メモリ |
|||
* 開始前の事前ステップ |
|||
|
|||
ここまでのドキュメントのチュートリアルでは、おそらくUvicornのような**サーバープログラム**を**単一のプロセス**で実行しています。 |
|||
|
|||
アプリケーションをデプロイする際には、**複数のコア**を利用し、そしてより多くのリクエストを処理できるようにするために、プロセスの**レプリケーション**を持つことを望むでしょう。 |
|||
|
|||
前のチャプターである[デプロイメントのコンセプト](./concepts.md){.internal-link target=_blank}にて見てきたように、有効な戦略がいくつかあります。 |
|||
|
|||
ここでは<a href="https://gunicorn.org/" class="external-link" target="_blank">**Gunicorn**</a>が**Uvicornのワーカー・プロセス**を管理する場合の使い方について紹介していきます。 |
|||
|
|||
!!! info |
|||
<!-- NOTE: the current version of docker.md is outdated compared to English one. --> |
|||
DockerやKubernetesなどのコンテナを使用している場合は、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](./docker.md){.internal-link target=_blank} |
|||
|
|||
特に**Kubernetes**上で実行する場合は、おそらく**Gunicornを使用せず**、**コンテナごとに単一のUvicornプロセス**を実行することになりますが、それについてはこの章の後半で説明します。 |
|||
|
|||
## GunicornによるUvicornのワーカー・プロセスの管理 |
|||
|
|||
**Gunicorn**は**WSGI標準**のアプリケーションサーバーです。このことは、GunicornはFlaskやDjangoのようなアプリケーションにサービスを提供できることを意味します。Gunicornそれ自体は**FastAPI**と互換性がないですが、というのもFastAPIは最新の**<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI 標準</a>**を使用しているためです。 |
|||
|
|||
しかし、Gunicornは**プロセスマネージャー**として動作し、ユーザーが特定の**ワーカー・プロセスクラス**を使用するように指示することができます。するとGunicornはそのクラスを使い1つ以上の**ワーカー・プロセス**を開始します。 |
|||
|
|||
そして**Uvicorn**には**Gunicorn互換のワーカークラス**があります。 |
|||
|
|||
この組み合わせで、Gunicornは**プロセスマネージャー**として動作し、**ポート**と**IP**をリッスンします。そして、**Uvicornクラス**を実行しているワーカー・プロセスに通信を**転送**します。 |
|||
|
|||
そして、Gunicorn互換の**Uvicornワーカー**クラスが、FastAPIが使えるように、Gunicornから送られてきたデータをASGI標準に変換する役割を担います。 |
|||
|
|||
## GunicornとUvicornをインストールする |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "uvicorn[standard]" gunicorn |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
これによりUvicornと(高性能を得るための)標準(`standard`)の追加パッケージとGunicornの両方がインストールされます。 |
|||
|
|||
## UvicornのワーカーとともにGunicornを実行する |
|||
|
|||
Gunicornを以下のように起動させることができます: |
|||
|
|||
<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> |
|||
|
|||
それぞれのオプションの意味を見てみましょう: |
|||
|
|||
* `main:app`: `main`は"`main`"という名前のPythonモジュール、つまりファイル`main.py`を意味します。そして `app` は **FastAPI** アプリケーションの変数名です。 |
|||
* main:app`はPythonの`import`文と同じようなものだと想像できます: |
|||
|
|||
```Python |
|||
from main import app |
|||
``` |
|||
|
|||
* つまり、`main:app`のコロンは、`from main import app`のPythonの`import`の部分と同じになります。 |
|||
|
|||
* `--workers`: 使用するワーカー・プロセスの数で、それぞれがUvicornのワーカーを実行します。 |
|||
|
|||
* `--worker-class`: ワーカー・プロセスで使用するGunicorn互換のワーカークラスです。 |
|||
* ここではGunicornがインポートして使用できるクラスを渡します: |
|||
|
|||
```Python |
|||
import uvicorn.workers.UvicornWorker |
|||
``` |
|||
|
|||
* `--bind`: GunicornにリッスンするIPとポートを伝えます。コロン(`:`)でIPとポートを区切ります。 |
|||
* Uvicornを直接実行している場合は、`--bind 0.0.0.0:80` (Gunicornのオプション)の代わりに、`--host 0.0.0.0`と `--port 80`を使います。 |
|||
|
|||
出力では、各プロセスの**PID**(プロセスID)が表示されているのがわかります(単なる数字です)。 |
|||
|
|||
以下の通りです: |
|||
|
|||
* Gunicornの**プロセス・マネージャー**はPID `19499`(あなたの場合は違う番号でしょう)で始まります。 |
|||
* 次に、`Listening at: http://0.0.0.0:80`を開始します。 |
|||
* それから `uvicorn.workers.UvicornWorker` でワーカークラスを使用することを検出します。 |
|||
* そして、**4つのワーカー**を起動します。それぞれのワーカーのPIDは、`19511`、`19513`、`19514`、`19515`です。 |
|||
|
|||
Gunicornはまた、ワーカーの数を維持するために必要であれば、**ダウンしたプロセス**を管理し、**新しいプロセスを**再起動**させます。そのため、上記のリストにある**再起動**の概念に一部役立ちます。 |
|||
|
|||
しかしながら、必要であればGunicornを**再起動**させ、**起動時に実行**させるなど、外部のコンポーネントを持たせることも必要かもしれません。 |
|||
|
|||
## Uvicornとワーカー |
|||
|
|||
Uvicornには複数の**ワーカー・プロセス**を起動し実行するオプションもあります。 |
|||
|
|||
とはいうものの、今のところUvicornのワーカー・プロセスを扱う機能はGunicornよりも制限されています。そのため、このレベル(Pythonレベル)でプロセスマネージャーを持ちたいのであれば、Gunicornをプロセスマネージャーとして使ってみた方が賢明かもしれないです。 |
|||
|
|||
どんな場合であれ、以下のように実行します: |
|||
|
|||
<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> |
|||
|
|||
ここで唯一の新しいオプションは `--workers` で、Uvicornに4つのワーカー・プロセスを起動するように指示しています。 |
|||
|
|||
各プロセスの **PID** が表示され、親プロセスの `27365` (これは **プロセスマネージャ**) と、各ワーカー・プロセスの **PID** が表示されます: `27368`、`27369`、`27370`、`27367`になります。 |
|||
|
|||
## デプロイメントのコンセプト |
|||
|
|||
ここでは、アプリケーションの実行を**並列化**し、CPUの**マルチコア**を活用し、**より多くのリクエスト**に対応できるようにするために、**Gunicorn**(またはUvicorn)を使用して**Uvicornワーカー・プロセス**を管理する方法を見ていきました。 |
|||
|
|||
上記のデプロイのコンセプトのリストから、ワーカーを使うことは主に**レプリケーション**の部分と、**再起動**を少し助けてくれます: |
|||
|
|||
* セキュリティ - HTTPS |
|||
* 起動時の実行 |
|||
* 再起動 |
|||
* レプリケーション(実行中のプロセス数) |
|||
* メモリー |
|||
* 開始前の事前のステップ |
|||
|
|||
|
|||
## コンテナとDocker |
|||
<!-- NOTE: the current version of docker.md is outdated compared to English one. --> |
|||
次章の[コンテナ内のFastAPI - Docker](./docker.md){.internal-link target=_blank}では、その他の**デプロイのコンセプト**を扱うために実施するであろう戦略をいくつか紹介します。 |
|||
|
|||
また、**GunicornとUvicornワーカー**を含む**公式Dockerイメージ**と、簡単なケースに役立ついくつかのデフォルト設定も紹介します。 |
|||
|
|||
また、(Gunicornを使わずに)Uvicornプロセスを1つだけ実行するために、**ゼロから独自のイメージを**構築する方法も紹介します。これは簡単なプロセスで、おそらく**Kubernetes**のような分散コンテナ管理システムを使うときにやりたいことでしょう。 |
|||
|
|||
## まとめ |
|||
|
|||
Uvicornワーカーを使ったプロセスマネージャとして**Gunicorn**(またはUvicorn)を使えば、**マルチコアCPU**を活用して**複数のプロセスを並列実行**できます。 |
|||
|
|||
これらのツールやアイデアは、**あなた自身のデプロイシステム**をセットアップしながら、他のデプロイコンセプトを自分で行う場合にも使えます。 |
|||
|
|||
次の章では、コンテナ(DockerやKubernetesなど)を使った**FastAPI**について学んでいきましょう。これらのツールには、他の**デプロイのコンセプト**も解決する簡単な方法があることがわかるでしょう。✨ |
@ -1,258 +0,0 @@ |
|||
# Implantação FastAPI na Deta |
|||
|
|||
Nessa seção você aprenderá sobre como realizar a implantação de uma aplicação **FastAPI** na <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> utilizando o plano gratuito. 🎁 |
|||
|
|||
Isso tudo levará aproximadamente **10 minutos**. |
|||
|
|||
!!! info "Informação" |
|||
<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> é uma patrocinadora do **FastAPI**. 🎉 |
|||
|
|||
## Uma aplicação **FastAPI** simples |
|||
|
|||
* Crie e entre em um diretório para a sua aplicação, por exemplo, `./fastapideta/`. |
|||
|
|||
### Código FastAPI |
|||
|
|||
* Crie o arquivo `main.py` com: |
|||
|
|||
```Python |
|||
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): |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
### Requisitos |
|||
|
|||
Agora, no mesmo diretório crie o arquivo `requirements.txt` com: |
|||
|
|||
```text |
|||
fastapi |
|||
``` |
|||
|
|||
!!! tip "Dica" |
|||
Você não precisa instalar Uvicorn para realizar a implantação na Deta, embora provavelmente queira instalá-lo para testar seu aplicativo localmente. |
|||
|
|||
### Estrutura de diretório |
|||
|
|||
Agora você terá o diretório `./fastapideta/` com dois arquivos: |
|||
|
|||
``` |
|||
. |
|||
└── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
## Crie uma conta gratuita na Deta |
|||
|
|||
Agora crie <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">uma conta gratuita na Deta</a>, você precisará apenas de um email e senha. |
|||
|
|||
Você nem precisa de um cartão de crédito. |
|||
|
|||
## Instale a CLI |
|||
|
|||
Depois de ter sua conta criada, instale Deta <abbr title="Interface de Linha de Comando">CLI</abbr>: |
|||
|
|||
=== "Linux, macOS" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ curl -fsSL https://get.deta.dev/cli.sh | sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ iwr https://get.deta.dev/cli.ps1 -useb | iex |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Após a instalação, abra um novo terminal para que a CLI seja detectada. |
|||
|
|||
Em um novo terminal, confirme se foi instalado corretamente com: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta --help |
|||
|
|||
Deta command line interface for managing deta micros. |
|||
Complete documentation available at https://docs.deta.sh |
|||
|
|||
Usage: |
|||
deta [flags] |
|||
deta [command] |
|||
|
|||
Available Commands: |
|||
auth Change auth settings for a deta micro |
|||
|
|||
... |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Dica" |
|||
Se você tiver problemas ao instalar a CLI, verifique a <a href="https://docs.deta.sh/docs/micros/getting_started?ref=fastapi" class="external-link" target="_blank">documentação oficial da Deta</a>. |
|||
|
|||
## Login pela CLI |
|||
|
|||
Agora faça login na Deta pela CLI com: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta login |
|||
|
|||
Please, log in from the web page. Waiting.. |
|||
Logged in successfully. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Isso abrirá um navegador da Web e autenticará automaticamente. |
|||
|
|||
## Implantação com Deta |
|||
|
|||
Em seguida, implante seu aplicativo com a Deta CLI: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta new |
|||
|
|||
Successfully created a new micro |
|||
|
|||
// Notice the "endpoint" 🔍 |
|||
|
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
|
|||
Adding dependencies... |
|||
|
|||
|
|||
---> 100% |
|||
|
|||
|
|||
Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Você verá uma mensagem JSON semelhante a: |
|||
|
|||
```JSON hl_lines="4" |
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "Dica" |
|||
Sua implantação terá um URL `"endpoint"` diferente. |
|||
|
|||
## Confira |
|||
|
|||
Agora, abra seu navegador na URL do `endpoint`. No exemplo acima foi `https://qltnci.deta.dev`, mas o seu será diferente. |
|||
|
|||
Você verá a resposta JSON do seu aplicativo FastAPI: |
|||
|
|||
```JSON |
|||
{ |
|||
"Hello": "World" |
|||
} |
|||
``` |
|||
|
|||
Agora vá para o `/docs` da sua API, no exemplo acima seria `https://qltnci.deta.dev/docs`. |
|||
|
|||
Ele mostrará sua documentação como: |
|||
|
|||
<img src="/img/deployment/deta/image01.png"> |
|||
|
|||
## Permitir acesso público |
|||
|
|||
Por padrão, a Deta lidará com a autenticação usando cookies para sua conta. |
|||
|
|||
Mas quando estiver pronto, você pode torná-lo público com: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta auth disable |
|||
|
|||
Successfully disabled http auth |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Agora você pode compartilhar essa URL com qualquer pessoa e elas conseguirão acessar sua API. 🚀 |
|||
|
|||
## HTTPS |
|||
|
|||
Parabéns! Você realizou a implantação do seu app FastAPI na Deta! 🎉 🍰 |
|||
|
|||
Além disso, observe que a Deta lida corretamente com HTTPS para você, para que você não precise cuidar disso e tenha a certeza de que seus clientes terão uma conexão criptografada segura. ✅ 🔒 |
|||
|
|||
## Verifique o Visor |
|||
|
|||
Na UI da sua documentação (você estará em um URL como `https://qltnci.deta.dev/docs`) envie um request para *operação de rota* `/items/{item_id}`. |
|||
|
|||
Por exemplo com ID `5`. |
|||
|
|||
Agora vá para <a href="https://web.deta.sh/" class="external-link" target="_blank">https://web.deta.sh</a>. |
|||
|
|||
Você verá que há uma seção à esquerda chamada <abbr title="it comes from Micro(server)">"Micros"</abbr> com cada um dos seus apps. |
|||
|
|||
Você verá uma aba com "Detalhes", e também a aba "Visor", vá para "Visor". |
|||
|
|||
Lá você pode inspecionar as solicitações recentes enviadas ao seu aplicativo. |
|||
|
|||
Você também pode editá-los e reproduzi-los novamente. |
|||
|
|||
<img src="/img/deployment/deta/image02.png"> |
|||
|
|||
## Saiba mais |
|||
|
|||
Em algum momento, você provavelmente desejará armazenar alguns dados para seu aplicativo de uma forma que persista ao longo do tempo. Para isso você pode usar <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a>, que também tem um generoso **nível gratuito**. |
|||
|
|||
Você também pode ler mais na <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">documentação da Deta</a>. |
|||
|
|||
## Conceitos de implantação |
|||
|
|||
Voltando aos conceitos que discutimos em [Deployments Concepts](./concepts.md){.internal-link target=_blank}, veja como cada um deles seria tratado com a Deta: |
|||
|
|||
* **HTTPS**: Realizado pela Deta, eles fornecerão um subdomínio e lidarão com HTTPS automaticamente. |
|||
* **Executando na inicialização**: Realizado pela Deta, como parte de seu serviço. |
|||
* **Reinicialização**: Realizado pela Deta, como parte de seu serviço. |
|||
* **Replicação**: Realizado pela Deta, como parte de seu serviço. |
|||
* **Memória**: Limite predefinido pela Deta, você pode contatá-los para aumentá-lo. |
|||
* **Etapas anteriores a inicialização**: Não suportado diretamente, você pode fazê-lo funcionar com o sistema Cron ou scripts adicionais. |
|||
|
|||
!!! note "Nota" |
|||
O Deta foi projetado para facilitar (e gratuitamente) a implantação rápida de aplicativos simples. |
|||
|
|||
Ele pode simplificar vários casos de uso, mas, ao mesmo tempo, não suporta outros, como o uso de bancos de dados externos (além do próprio sistema de banco de dados NoSQL da Deta), máquinas virtuais personalizadas, etc. |
|||
|
|||
Você pode ler mais detalhes na <a href="https://docs.deta.sh/docs/micros/about/" class="external-link" target="_blank">documentação da Deta</a> para ver se é a escolha certa para você. |
@ -0,0 +1,700 @@ |
|||
# FastAPI и Docker-контейнеры |
|||
|
|||
При развёртывании приложений FastAPI, часто начинают с создания **образа контейнера на основе Linux**. Обычно для этого используют <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Затем можно развернуть такой контейнер на сервере одним из нескольких способов. |
|||
|
|||
Использование контейнеров на основе Linux имеет ряд преимуществ, включая **безопасность**, **воспроизводимость**, **простоту** и прочие. |
|||
|
|||
!!! tip "Подсказка" |
|||
Торопитесь или уже знакомы с этой технологией? Перепрыгните на раздел [Создать Docker-образ для FastAPI 👇](#docker-fastapi) |
|||
|
|||
<details> |
|||
<summary>Развернуть Dockerfile 👀</summary> |
|||
|
|||
```Dockerfile |
|||
FROM python:3.9 |
|||
|
|||
WORKDIR /code |
|||
|
|||
COPY ./requirements.txt /code/requirements.txt |
|||
|
|||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
|||
|
|||
COPY ./app /code/app |
|||
|
|||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
|||
|
|||
# Если используете прокси-сервер, такой как Nginx или Traefik, добавьте --proxy-headers |
|||
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"] |
|||
``` |
|||
|
|||
</details> |
|||
|
|||
## Что такое "контейнер" |
|||
|
|||
Контейнеризация - это **легковесный** способ упаковать приложение, включая все его зависимости и необходимые файлы, чтобы изолировать его от других контейнеров (других приложений и компонентов) работающих на этой же системе. |
|||
|
|||
Контейнеры, основанные на Linux, запускаются используя ядро Linux хоста (машины, виртуальной машины, облачного сервера и т.п.). Это значит, что они очень легковесные (по сравнению с полноценными виртуальными машинами, полностью эмулирующими работу операционной системы). |
|||
|
|||
Благодаря этому, контейнеры потребляют **малое количество ресурсов**, сравнимое с процессом запущенным напрямую (виртуальная машина потребует гораздо больше ресурсов). |
|||
|
|||
Контейнеры также имеют собственные запущенные **изолированные** процессы (но часто только один процесс), файловую систему и сеть, что упрощает развёртывание, разработку, управление доступом и т.п. |
|||
|
|||
## Что такое "образ контейнера" |
|||
|
|||
Для запуска **контейнера** нужен **образ контейнера**. |
|||
|
|||
Образ контейнера - это **замороженная** версия всех файлов, переменных окружения, программ и команд по умолчанию, необходимых для работы приложения. **Замороженный** - означает, что **образ** не запущен и не выполняется, это всего лишь упакованные вместе файлы и метаданные. |
|||
|
|||
В отличие от **образа контейнера**, хранящего неизменное содержимое, под термином **контейнер** подразумевают запущенный образ, то есть объёкт, который **исполняется**. |
|||
|
|||
Когда **контейнер** запущен (на основании **образа**), он может создавать и изменять файлы, переменные окружения и т.д. Эти изменения будут существовать только внутри контейнера, но не будут сохраняться в образе контейнера (не будут сохранены на диск). |
|||
|
|||
Образ контейнера можно сравнить с файлом, содержащем **программу**, например, как файл `main.py`. |
|||
|
|||
И **контейнер** (в отличие от **образа**) - это на самом деле выполняемый экземпляр образа, примерно как **процесс**. По факту, контейнер запущен только когда запущены его процессы (чаще, всего один процесс) и остановлен, когда запущенных процессов нет. |
|||
|
|||
## Образы контейнеров |
|||
|
|||
Docker является одним оз основных инструментов для создания **образов** и **контейнеров** и управления ими. |
|||
|
|||
Существует общедоступный <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> с подготовленными **официальными образами** многих инструментов, окружений, баз данных и приложений. |
|||
|
|||
К примеру, есть официальный <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">образ Python</a>. |
|||
|
|||
Также там представлены и другие полезные образы, такие как базы данных: |
|||
|
|||
* <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> |
|||
|
|||
и т.п. |
|||
|
|||
Использование подготовленных образов значительно упрощает **комбинирование** и использование разных инструментов. Например, Вы можете попытаться использовать новую базу данных. В большинстве случаев можно использовать **официальный образ** и всего лишь указать переменные окружения. |
|||
|
|||
Таким образом, Вы можете изучить, что такое контейнеризация и Docker, и использовать полученные знания с разными инструментами и компонентами. |
|||
|
|||
Так, Вы можете запустить одновременно **множество контейнеров** с базой данных, Python-приложением, веб-сервером, React-приложением и соединить их вместе через внутреннюю сеть. |
|||
|
|||
Все системы управления контейнерами (такие, как Docker или Kubernetes) имеют встроенные возможности для организации такого сетевого взаимодействия. |
|||
|
|||
## Контейнеры и процессы |
|||
|
|||
Обычно **образ контейнера** содержит метаданные предустановленной программы или команду, которую следует выполнить при запуске **контейнера**. Также он может содержать параметры, передаваемые предустановленной программе. Похоже на то, как если бы Вы запускали такую программу через терминал. |
|||
|
|||
Когда **контейнер** запущен, он будет выполнять прописанные в нём команды и программы. Но Вы можете изменить его так, чтоб он выполнял другие команды и программы. |
|||
|
|||
Контейнер буде работать до тех пор, пока выполняется его **главный процесс** (команда или программа). |
|||
|
|||
В контейнере обычно выполняется **только один процесс**, но от его имени можно запустить другие процессы, тогда в этом же в контейнере будет выполняться **множество процессов**. |
|||
|
|||
Контейнер не считается запущенным, если в нём **не выполняется хотя бы один процесс**. Если главный процесс остановлен, значит и контейнер остановлен. |
|||
|
|||
## Создать Docker-образ для FastAPI |
|||
|
|||
Что ж, давайте ужё создадим что-нибудь! 🚀 |
|||
|
|||
Я покажу Вам, как собирать **Docker-образ** для FastAPI **с нуля**, основываясь на **официальном образе Python**. |
|||
|
|||
Такой подход сгодится для **большинства случаев**, например: |
|||
|
|||
* Использование с **Kubernetes** или аналогичным инструментом |
|||
* Запуск в **Raspberry Pi** |
|||
* Использование в облачных сервисах, запускающих образы контейнеров для Вас и т.п. |
|||
|
|||
### Установить зависимости |
|||
|
|||
Обычно Вашему приложению необходимы **дополнительные библиотеки**, список которых находится в отдельном файле. |
|||
|
|||
На название и содержание такого файла влияет выбранный Вами инструмент **установки** этих библиотек (зависимостей). |
|||
|
|||
Чаще всего это простой файл `requirements.txt` с построчным перечислением библиотек и их версий. |
|||
|
|||
При этом Вы, для выбора версий, будете использовать те же идеи, что упомянуты на странице [О версиях FastAPI](./versions.md){.internal-link target=_blank}. |
|||
|
|||
Ваш файл `requirements.txt` может выглядеть как-то так: |
|||
|
|||
``` |
|||
fastapi>=0.68.0,<0.69.0 |
|||
pydantic>=1.8.0,<2.0.0 |
|||
uvicorn>=0.15.0,<0.16.0 |
|||
``` |
|||
|
|||
Устанавливать зависимости проще всего с помощью `pip`: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install -r requirements.txt |
|||
---> 100% |
|||
Successfully installed fastapi pydantic uvicorn |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! info "Информация" |
|||
Существуют и другие инструменты управления зависимостями. |
|||
|
|||
В этом же разделе, но позже, я покажу Вам пример использования Poetry. 👇 |
|||
|
|||
### Создать приложение **FastAPI** |
|||
|
|||
* Создайте директорию `app` и перейдите в неё. |
|||
* Создайте пустой файл `__init__.py`. |
|||
* Создайте файл `main.py` и заполните его: |
|||
|
|||
```Python |
|||
from typing import Union |
|||
|
|||
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: Union[str, None] = None): |
|||
return {"item_id": item_id, "q": q} |
|||
``` |
|||
|
|||
### Dockerfile |
|||
|
|||
В этой же директории создайте файл `Dockerfile` и заполните его: |
|||
|
|||
```{ .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. Начните с официального образа Python, который будет основой для образа приложения. |
|||
|
|||
2. Укажите, что в дальнейшем команды запускаемые в контейнере, будут выполняться в директории `/code`. |
|||
|
|||
Инструкция создаст эту директорию внутри контейнера и мы поместим в неё файл `requirements.txt` и директорию `app`. |
|||
|
|||
3. Скопируете файл с зависимостями из текущей директории в `/code`. |
|||
|
|||
Сначала копируйте **только** файл с зависимостями. |
|||
|
|||
Этот файл **изменяется довольно редко**, Docker ищет изменения при постройке образа и если не находит, то использует **кэш**, в котором хранятся предыдущии версии сборки образа. |
|||
|
|||
4. Установите библиотеки перечисленные в файле с зависимостями. |
|||
|
|||
Опция `--no-cache-dir` указывает `pip` не сохранять загружаемые библиотеки на локальной машине для использования их в случае повторной загрузки. В контейнере, в случае пересборки этого шага, они всё равно будут удалены. |
|||
|
|||
!!! note "Заметка" |
|||
Опция `--no-cache-dir` нужна только для `pip`, она никак не влияет на Docker или контейнеры. |
|||
|
|||
Опция `--upgrade` указывает `pip` обновить библиотеки, емли они уже установлены. |
|||
|
|||
Ка и в предыдущем шаге с копированием файла, этот шаг также будет использовать **кэш Docker** в случае отсутствия изменений. |
|||
|
|||
Использрвание кэша, особенно на этом шаге, позволит Вам **сэкономить** кучу времени при повторной сборке образа, так как зависимости будут сохранены в кеше, а не **загружаться и устанавливаться каждый раз**. |
|||
|
|||
5. Скопируйте директорию `./app` внутрь директории `/code` (в контейнере). |
|||
|
|||
Так как в этой директории расположен код, который **часто изменяется**, то использование **кэша** на этом шаге будет наименее эффективно, а значит лучше поместить этот шаг **ближе к концу** `Dockerfile`, дабы не терять выгоду от оптимизации предыдущих шагов. |
|||
|
|||
6. Укажите **команду**, запускающую сервер `uvicorn`. |
|||
|
|||
`CMD` принимает список строк, разделённых запятыми, но при выполнении объединит их через пробел, собрав из них одну команду, которую Вы могли бы написать в терминале. |
|||
|
|||
Эта команда будет выполнена в **текущей рабочей директории**, а именно в директории `/code`, котоая указана командой `WORKDIR /code`. |
|||
|
|||
Так как команда выполняется внутрии директории `/code`, в которую мы поместили папку `./app` с приложением, то **Uvicorn** сможет найти и **импортировать** объект `app` из файла `app.main`. |
|||
|
|||
!!! tip "Подсказка" |
|||
Если ткнёте на кружок с плюсом, то увидите пояснения. 👆 |
|||
|
|||
На данном этапе структура проекта должны выглядеть так: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ └── main.py |
|||
├── Dockerfile |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
#### Использование прокси-сервера |
|||
|
|||
Если Вы запускаете контейнер за прокси-сервером завершения TLS (балансирующего нагрузку), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`, которая укажет Uvicorn, что он работает позади прокси-сервера и может доверять заголовкам отправляемым им. |
|||
|
|||
```Dockerfile |
|||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
#### Кэш Docker'а |
|||
|
|||
В нашем `Dockerfile` использована полезная хитрость, когда сначала копируется **только файл с зависимостями**, а не вся папка с кодом приложения. |
|||
|
|||
```Dockerfile |
|||
COPY ./requirements.txt /code/requirements.txt |
|||
``` |
|||
|
|||
Docker и подобные ему инструменты **создают** образы контейнеров **пошагово**, добавляя **один слой над другим**, начиная с первой строки `Dockerfile` и добавляя файлы, создаваемые при выполнении каждой инструкции из `Dockerfile`. |
|||
|
|||
При создании образа используется **внутренний кэш** и если в файлах нет изменений с момента последней сборки образа, то будет **переиспользован** ранее созданный слой образа, а не повторное копирование файлов и создание слоя с нуля. |
|||
Заметьте, что так как слой следующего шага зависит от слоя предыдущего, то изменения внесённые в промежуточный слой, также повлияют на последующие. |
|||
|
|||
Избегание копирования файлов не обязательно улучшит ситуацию, но использование кэша на одном шаге, позволит **использовать кэш и на следующих шагах**. Например, можно использовать кэш при установке зависимостей: |
|||
|
|||
```Dockerfile |
|||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
|||
``` |
|||
|
|||
Файл со списком зависимостей **изменяется довольно редко**. Так что выполнив команду копирования только этого файла, Docker сможет **использовать кэш** на этом шаге. |
|||
|
|||
А затем **использовать кэш и на следующем шаге**, загружающем и устанавливающем зависимости. И вот тут-то мы и **сэкономим много времени**. ✨ ...а не будем томиться в тягостном ожидании. 😪😆 |
|||
|
|||
Для загрузки и установки необходимых библиотек **может понадобиться несколько минут**, но использование **кэша** занимает несколько **секунд** максимум. |
|||
|
|||
И так как во время разработки Вы будете часто пересобирать контейнер для проверки работоспособности внесённых изменений, то сэкономленные минуты сложатся в часы, а то и дни. |
|||
|
|||
Так как папка с кодом приложения **изменяется чаще всего**, то мы расположили её в конце `Dockerfile`, ведь после внесённых в код изменений кэш не будет использован на этом и следующих шагах. |
|||
|
|||
```Dockerfile |
|||
COPY ./app /code/app |
|||
``` |
|||
|
|||
### Создать Docker-образ |
|||
|
|||
Теперь, когда все файлы на своих местах, давайте создадим образ контейнера. |
|||
|
|||
* Перейдите в директорию проекта (в ту, где расположены `Dockerfile` и папка `app` с приложением). |
|||
* Создай образ приложения FastAPI: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ docker build -t myimage . |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Подсказка" |
|||
Обратите внимание, что в конце написана точка - `.`, это то же самое что и `./`, тем самым мы указываем Docker директорию, из которой нужно выполнять сборку образа контейнера. |
|||
|
|||
В данном случае это та же самая директория (`.`). |
|||
|
|||
### Запуск Docker-контейнера |
|||
|
|||
* Запустите контейнер, основанный на Вашем образе: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ docker run -d --name mycontainer -p 80:80 myimage |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## Проверка |
|||
|
|||
Вы можете проверить, что Ваш Docker-контейнер работает перейдя по ссылке: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> или <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (или похожей, которую использует Ваш Docker-хост). |
|||
|
|||
Там Вы увидите: |
|||
|
|||
```JSON |
|||
{"item_id": 5, "q": "somequery"} |
|||
``` |
|||
|
|||
## Интерактивная документация API |
|||
|
|||
Теперь перейдите по ссылке <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> или <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (или похожей, которую использует Ваш Docker-хост). |
|||
|
|||
Здесь Вы увидите автоматическую интерактивную документацию API (предоставляемую <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>): |
|||
|
|||
 |
|||
|
|||
## Альтернативная документация API |
|||
|
|||
Также Вы можете перейти по ссылке <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> or <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (или похожей, которую использует Ваш Docker-хост). |
|||
|
|||
Здесь Вы увидите альтернативную автоматическую документацию API (предоставляемую <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>): |
|||
|
|||
 |
|||
|
|||
## Создание Docker-образа на основе однофайлового приложения FastAPI |
|||
|
|||
Если Ваше приложение FastAPI помещено в один файл, например, `main.py` и структура Ваших файлов похожа на эту: |
|||
|
|||
``` |
|||
. |
|||
├── Dockerfile |
|||
├── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
Вам нужно изменить в `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. Скопируйте непосредственно файл `main.py` в директорию `/code` (не указывайте `./app`). |
|||
|
|||
2. При запуске Uvicorn укажите ему, что объект `app` нужно импортировать из файла `main` (вместо импортирования из `app.main`). |
|||
|
|||
Настройте Uvicorn на использование `main` вместо `app.main` для импорта объекта `app`. |
|||
|
|||
## Концепции развёртывания |
|||
|
|||
Давайте вспомним о [Концепциях развёртывания](./concepts.md){.internal-link target=_blank} и применим их к контейнерам. |
|||
|
|||
Контейнеры - это, в основном, инструмент упрощающий **сборку и развёртывание** приложения и они не обязыают к применению какой-то определённой **концепции развёртывания**, а значит мы можем выбирать нужную стратегию. |
|||
|
|||
**Хорошая новость** в том, что независимо от выбранной стратегии, мы всё равно можем покрыть все концепции развёртывания. 🎉 |
|||
|
|||
Рассмотрим эти **концепции развёртывания** применительно к контейнерам: |
|||
|
|||
* Использование более безопасного протокола HTTPS |
|||
* Настройки запуска приложения |
|||
* Перезагрузка приложения |
|||
* Запуск нескольких экземпляров приложения |
|||
* Управление памятью |
|||
* Использование перечисленных функций перед запуском приложения |
|||
|
|||
## Использование более безопасного протокола HTTPS |
|||
|
|||
Если мы определимся, что **образ контейнера** будет содержать только приложение FastAPI, то работу с HTTPS можно организовать **снаружи** контейнера при помощи другого инструмента. |
|||
|
|||
Это может быть другой контейнер, в котором есть, например, <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, работающий с **HTTPS** и **самостоятельно** обновляющий **сертификаты**. |
|||
|
|||
!!! tip "Подсказка" |
|||
Traefik совместим с Docker, Kubernetes и им подобными инструментами. Он очень прост в установке и настройке использования HTTPS для Ваших контейнеров. |
|||
|
|||
В качестве альтернативы, работу с HTTPS можно доверить облачному провайдеру, если он предоставляет такую услугу. |
|||
|
|||
## Настройки запуска и перезагрузки приложения |
|||
|
|||
Обычно **запуском контейнера с приложением** занимается какой-то отдельный инструмент. |
|||
|
|||
Это может быть сам **Docker**, **Docker Compose**, **Kubernetes**, **облачный провайдер** и т.п. |
|||
|
|||
В большинстве случаев это простейшие настройки запуска и перезагрузки приложения (при падении). Например, команде запуска Docker-контейнера можно добавить опцию `--restart`. |
|||
|
|||
Управление запуском и перезагрузкой приложений без использования контейнеров - весьма затруднительно. Но при **работе с контейнерами** - это всего лишь функционал доступный по умолчанию. ✨ |
|||
|
|||
## Запуск нескольких экземпляров приложения - Указание количества процессов |
|||
|
|||
Если у Вас есть <abbr title="Несколько серверов настроенных для совместной работы.">кластер</abbr> машин под управлением **Kubernetes**, Docker Swarm Mode, Nomad или аналогичной сложной системой оркестрации контейнеров, скорее всего, вместо использования менеджера процессов (типа Gunicorn и его воркеры) в каждом контейнере, Вы захотите **управлять количеством запущенных экземпляров приложения** на **уровне кластера**. |
|||
|
|||
В любую из этих систем управления контейнерами обычно встроен способ управления **количеством запущенных контейнеров** для распределения **нагрузки** от входящих запросов на **уровне кластера**. |
|||
|
|||
В такой ситуации Вы, вероятно, захотите создать **образ Docker**, как [описано выше](#dockerfile), с установленными зависимостями и запускающий **один процесс Uvicorn** вместо того, чтобы запускать Gunicorn управляющий несколькими воркерами Uvicorn. |
|||
|
|||
### Балансировщик нагрузки |
|||
|
|||
Обычно при использовании контейнеров один компонент **прослушивает главный порт**. Это может быть контейнер содержащий **прокси-сервер завершения работы TLS** для работы с **HTTPS** или что-то подобное. |
|||
|
|||
Поскольку этот компонент **принимает запросы** и равномерно **распределяет** их между компонентами, его также называют **балансировщиком нагрузки**. |
|||
|
|||
!!! tip "Подсказка" |
|||
**Прокси-сервер завершения работы TLS** одновременно может быть **балансировщиком нагрузки**. |
|||
|
|||
Система оркестрации, которую Вы используете для запуска и управления контейнерами, имеет встроенный инструмент **сетевого взаимодействия** (например, для передачи HTTP-запросов) между контейнерами с Вашими приложениями и **балансировщиком нагрузки** (который также может быть **прокси-сервером**). |
|||
|
|||
### Один балансировщик - Множество контейнеров |
|||
|
|||
При работе с **Kubernetes** или аналогичными системами оркестрации использование их внутреннней сети позволяет иметь один **балансировщик нагрузки**, который прослушивает **главный** порт и передаёт запросы **множеству запущенных контейнеров** с Вашими приложениями. |
|||
|
|||
В каждом из контейнеров обычно работает **только один процесс** (например, процесс Uvicorn управляющий Вашим приложением FastAPI). Контейнеры могут быть **идентичными**, запущенными на основе одного и того же образа, но у каждого будут свои отдельные процесс, память и т.п. Таким образом мы получаем преимущества **распараллеливания** работы по **разным ядрам** процессора или даже **разным машинам**. |
|||
|
|||
Система управления контейнерами с **балансировщиком нагрузки** будет **распределять запросы** к контейнерам с приложениями **по очереди**. То есть каждый запрос будет обработан одним из множества **одинаковых контейнеров** с одним и тем же приложением. |
|||
|
|||
**Балансировщик нагрузки** может обрабатывать запросы к *разным* приложениям, расположенным в Вашем кластере (например, если у них разные домены или префиксы пути) и передавать запросы нужному контейнеру с требуемым приложением. |
|||
|
|||
### Один процесс на контейнер |
|||
|
|||
В этом варианте **в одном контейнере будет запущен только один процесс (Uvicorn)**, а управление изменением количества запущенных копий приложения происходит на уровне кластера. |
|||
|
|||
Здесь **не нужен** менеджер процессов типа Gunicorn, управляющий процессами Uvicorn, или же Uvicorn, управляющий другими процессами Uvicorn. Достаточно **только одного процесса Uvicorn** на контейнер (но запуск нескольких процессов не запрещён). |
|||
|
|||
Использование менеджера процессов (Gunicorn или Uvicorn) внутри контейнера только добавляет **излишнее усложнение**, так как управление следует осуществлять системой оркестрации. |
|||
|
|||
### <a name="special-cases"></a>Множество процессов внутри контейнера для особых случаев</a> |
|||
|
|||
Безусловно, бывают **особые случаи**, когда может понадобится внутри контейнера запускать **менеджер процессов Gunicorn**, управляющий несколькими **процессами Uvicorn**. |
|||
|
|||
Для таких случаев Вы можете использовать **официальный Docker-образ** (прим. пер: - *здесь и далее на этой странице, если Вы встретите сочетание "официальный Docker-образ" без уточнений, то автор имеет в виду именно предоставляемый им образ*), где в качестве менеджера процессов используется **Gunicorn**, запускающий несколько **процессов Uvicorn** и некоторые настройки по умолчанию, автоматически устанавливающие количество запущенных процессов в зависимости от количества ядер Вашего процессора. Я расскажу Вам об этом подробнее тут: [Официальный Docker-образ со встроенными Gunicorn и Uvicorn](#docker-gunicorn-uvicorn). |
|||
|
|||
Некоторые примеры подобных случаев: |
|||
|
|||
#### Простое приложение |
|||
|
|||
Вы можете использовать менеджер процессов внутри контейнера, если Ваше приложение **настолько простое**, что у Вас нет необходимости (по крайней мере, пока нет) в тщательных настройках количества процессов и Вам достаточно имеющихся настроек по умолчанию (если используется официальный Docker-образ) для запуска приложения **только на одном сервере**, а не в кластере. |
|||
|
|||
#### Docker Compose |
|||
|
|||
С помощью **Docker Compose** можно разворачивать несколько контейнеров на **одном сервере** (не кластере), но при это у Вас не будет простого способа управления количеством запущенных контейнеров с одновременным сохранением общей сети и **балансировки нагрузки**. |
|||
|
|||
В этом случае можно использовать **менеджер процессов**, управляющий **несколькими процессами**, внутри **одного контейнера**. |
|||
|
|||
#### Prometheus и прочие причины |
|||
|
|||
У Вас могут быть и **другие причины**, когда использование **множества процессов** внутри **одного контейнера** будет проще, нежели запуск **нескольких контейнеров** с **единственным процессом** в каждом из них. |
|||
|
|||
Например (в зависимости от конфигурации), у Вас могут быть инструменты подобные экспортёру Prometheus, которые должны иметь доступ к **каждому запросу** приходящему в контейнер. |
|||
|
|||
Если у Вас будет **несколько контейнеров**, то Prometheus, по умолчанию, **при сборе метрик** получит их **только с одного контейнера**, который обрабатывает конкретный запрос, вместо **сбора метрик** со всех работающих контейнеров. |
|||
|
|||
В таком случае может быть проще иметь **один контейнер** со **множеством процессов**, с нужным инструментом (таким как экспортёр Prometheus) в этом же контейнере и собирающем метрики со всех внутренних процессов этого контейнера. |
|||
|
|||
--- |
|||
|
|||
Самое главное - **ни одно** из перечисленных правил не является **высеченным в камне** и Вы не обязаны слепо их повторять. Вы можете использовать эти идеи при **рассмотрении Вашего конкретного случая** и самостоятельно решать, какая из концепции подходит лучше: |
|||
|
|||
* Использование более безопасного протокола HTTPS |
|||
* Настройки запуска приложения |
|||
* Перезагрузка приложения |
|||
* Запуск нескольких экземпляров приложения |
|||
* Управление памятью |
|||
* Использование перечисленных функций перед запуском приложения |
|||
|
|||
## Управление памятью |
|||
|
|||
При **запуске одного процесса на контейнер** Вы получаете относительно понятный, стабильный и ограниченный объём памяти, потребляемый одним контейнером. |
|||
|
|||
Вы можете установить аналогичные ограничения по памяти при конфигурировании своей системы управления контейнерами (например, **Kubernetes**). Таким образом система сможет **изменять количество контейнеров** на **доступных ей машинах** приводя в соответствие количество памяти нужной контейнерам с количеством памяти доступной в кластере (наборе доступных машин). |
|||
|
|||
Если у Вас **простенькое** приложение, вероятно у Вас не будет **необходимости** устанавливать жёсткие ограничения на выделяемую ему память. Но если приложение **использует много памяти** (например, оно использует модели **машинного обучения**), Вам следует проверить, как много памяти ему требуется и отрегулировать **количество контейнеров** запущенных на **каждой машине** (может быть даже добавить машин в кластер). |
|||
|
|||
Если Вы запускаете **несколько процессов в контейнере**, то должны быть уверены, что эти процессы не **займут памяти больше**, чем доступно для контейнера. |
|||
|
|||
## Подготовительные шаги при запуске контейнеров |
|||
|
|||
Есть два основных подхода, которые Вы можете использовать при запуске контейнеров (Docker, Kubernetes и т.п.). |
|||
|
|||
### Множество контейнеров |
|||
|
|||
Когда Вы запускаете **множество контейнеров**, в каждом из которых работает **только один процесс** (например, в кластере **Kubernetes**), может возникнуть необходимость иметь **отдельный контейнер**, который осуществит **предварительные шаги перед запуском** остальных контейнеров (например, применяет миграции к базе данных). |
|||
|
|||
!!! info "Информация" |
|||
При использовании Kubernetes, это может быть <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Инициализирующий контейнер</a>. |
|||
|
|||
При отсутствии такой необходимости (допустим, не нужно применять миграции к базе данных, а только проверить, что она готова принимать соединения), Вы можете проводить такую проверку в каждом контейнере перед запуском его основного процесса и запускать все контейнеры **одновременно**. |
|||
|
|||
### Только один контейнер |
|||
|
|||
Если у Вас несложное приложение для работы которого достаточно **одного контейнера**, но в котором работает **несколько процессов** (или один процесс), то прохождение предварительных шагов можно осуществить в этом же контейнере до запуска основного процесса. Официальный Docker-образ поддерживает такие действия. |
|||
|
|||
## Официальный Docker-образ с Gunicorn и Uvicorn |
|||
|
|||
Я подготовил для Вас Docker-образ, в который включён Gunicorn управляющий процессами (воркерами) Uvicorn, в соответствии с концепциями рассмотренными в предыдущей главе: [Рабочие процессы сервера (воркеры) - Gunicorn совместно с Uvicorn](./server-workers.md){.internal-link target=_blank}. |
|||
|
|||
Этот образ может быть полезен для ситуаций описанных тут: [Множество процессов внутри контейнера для особых случаев](#special-cases). |
|||
|
|||
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. |
|||
|
|||
!!! warning "Предупреждение" |
|||
Скорее всего у Вас **нет необходимости** в использовании этого образа или подобного ему и лучше создать свой образ с нуля как описано тут: [Создать Docker-образ для FastAPI](#docker-fastapi). |
|||
|
|||
В этом образе есть **автоматический** механизм подстройки для запуска **необходимого количества процессов** в соответствии с доступным количеством ядер процессора. |
|||
|
|||
В нём установлены **разумные значения по умолчанию**, но можно изменять и обновлять конфигурацию с помощью **переменных окружения** или конфигурационных файлов. |
|||
|
|||
Он также поддерживает прохождение <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**Подготовительных шагов при запуске контейнеров**</a> при помощи скрипта. |
|||
|
|||
!!! tip "Подсказка" |
|||
Для просмотра всех возможных настроек перейдите на страницу этого Docker-образа: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. |
|||
|
|||
### Количество процессов в официальном Docker-образе |
|||
|
|||
**Количество процессов** в этом образе **вычисляется автоматически** и зависит от доступного количества **ядер** центрального процессора. |
|||
|
|||
Это означает, что он будет пытаться **выжать** из процессора как можно больше **производительности**. |
|||
|
|||
Но Вы можете изменять и обновлять конфигурацию с помощью **переменных окружения** и т.п. |
|||
|
|||
Поскольку количество процессов зависит от процессора, на котором работает контейнер, **объём потребляемой памяти** также будет зависеть от этого. |
|||
|
|||
А значит, если Вашему приложению требуется много оперативной памяти (например, оно использует модели машинного обучения) и Ваш сервер имеет центральный процессор с большим количеством ядер, но **не слишком большим объёмом оперативной памяти**, то может дойти до того, что контейнер попытается занять памяти больше, чем доступно, из-за чего будет падение производительности (или сервер вовсе упадёт). 🚨 |
|||
|
|||
|
|||
### Написание `Dockerfile` |
|||
|
|||
Итак, теперь мы можем написать `Dockerfile` основанный на этом официальном Docker-образе: |
|||
|
|||
```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 |
|||
``` |
|||
|
|||
### Большие приложения |
|||
|
|||
Если Вы успели ознакомиться с разделом [Приложения содержащие много файлов](../tutorial/bigger-applications.md){.internal-link target=_blank}, состоящие из множества файлов, Ваш Dockerfile может выглядеть так: |
|||
|
|||
```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 |
|||
``` |
|||
|
|||
### Как им пользоваться |
|||
|
|||
Если Вы используете **Kubernetes** (или что-то вроде того), скорее всего Вам **не нужно** использовать официальный Docker-образ (или другой похожий) в качестве основы, так как управление **количеством запущенных контейнеров** должно быть настроено на уровне кластера. В таком случае лучше **создать образ с нуля**, как описано в разделе Создать [Docker-образ для FastAPI](#docker-fastapi). |
|||
|
|||
Официальный образ может быть полезен в отдельных случаях, описанных выше в разделе [Множество процессов внутри контейнера для особых случаев](#special-cases). Например, если Ваше приложение **достаточно простое**, не требует запуска в кластере и способно уместиться в один контейнер, то его настройки по умолчанию будут работать довольно хорошо. Или же Вы развертываете его с помощью **Docker Compose**, работаете на одном сервере и т. д |
|||
|
|||
## Развёртывание образа контейнера |
|||
|
|||
После создания образа контейнера существует несколько способов его развёртывания. |
|||
|
|||
Например: |
|||
|
|||
* С использованием **Docker Compose** при развёртывании на одном сервере |
|||
* С использованием **Kubernetes** в кластере |
|||
* С использованием режима Docker Swarm в кластере |
|||
* С использованием других инструментов, таких как Nomad |
|||
* С использованием облачного сервиса, который будет управлять разворачиванием Вашего контейнера |
|||
|
|||
## Docker-образ и Poetry |
|||
|
|||
Если Вы пользуетесь <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> для управления зависимостями Вашего проекта, то можете использовать многоэтапную сборку образа: |
|||
|
|||
```{ .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. Это первый этап, которому мы дадим имя `requirements-stage`. |
|||
|
|||
2. Установите директорию `/tmp` в качестве рабочей директории. |
|||
|
|||
В ней будет создан файл `requirements.txt` |
|||
|
|||
3. На этом шаге установите Poetry. |
|||
|
|||
4. Скопируйте файлы `pyproject.toml` и `poetry.lock` в директорию `/tmp`. |
|||
|
|||
Поскольку название файла написано как `./poetry.lock*` (с `*` в конце), то ничего не сломается, если такой файл не будет найден. |
|||
|
|||
5. Создайте файл `requirements.txt`. |
|||
|
|||
6. Это второй (и последний) этап сборки, который и создаст окончательный образ контейнера. |
|||
|
|||
7. Установите директорию `/code` в качестве рабочей. |
|||
|
|||
8. Скопируйте файл `requirements.txt` в директорию `/code`. |
|||
|
|||
Этот файл находится в образе, созданном на предыдущем этапе, которому мы дали имя requirements-stage, потому при копировании нужно написать `--from-requirements-stage`. |
|||
|
|||
9. Установите зависимости, указанные в файле `requirements.txt`. |
|||
|
|||
10. Скопируйте папку `app` в папку `/code`. |
|||
|
|||
11. Запустите `uvicorn`, указав ему использовать объект `app`, расположенный в `app.main`. |
|||
|
|||
!!! tip "Подсказка" |
|||
Если ткнёте на кружок с плюсом, то увидите объяснения, что происходит в этой строке. |
|||
|
|||
**Этапы сборки Docker-образа** являются частью `Dockerfile` и работают как **временные образы контейнеров**. Они нужны только для создания файлов, используемых в дальнейших этапах. |
|||
|
|||
Первый этап был нужен только для **установки Poetry** и **создания файла `requirements.txt`**, в которым прописаны зависимости Вашего проекта, взятые из файла `pyproject.toml`. |
|||
|
|||
На **следующем этапе** `pip` будет использовать файл `requirements.txt`. |
|||
|
|||
В итоговом образе будет содержаться **только последний этап сборки**, предыдущие этапы будут отброшены. |
|||
|
|||
При использовании Poetry, имеет смысл использовать **многоэтапную сборку Docker-образа**, потому что на самом деле Вам не нужен Poetry и его зависимости в окончательном образе контейнера, Вам **нужен только** сгенерированный файл `requirements.txt` для установки зависимостей Вашего проекта. |
|||
|
|||
А на последнем этапе, придерживаясь описанных ранее правил, создаётся итоговый образ |
|||
|
|||
### Использование прокси-сервера завершения TLS и Poetry |
|||
|
|||
И снова повторюсь, если используете прокси-сервер (балансировщик нагрузки), такой, как Nginx или Traefik, добавьте в команду запуска опцию `--proxy-headers`: |
|||
|
|||
```Dockerfile |
|||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
## Резюме |
|||
|
|||
При помощи систем контейнеризации (таких, как **Docker** и **Kubernetes**), становится довольно просто обрабатывать все **концепции развертывания**: |
|||
|
|||
* Использование более безопасного протокола HTTPS |
|||
* Настройки запуска приложения |
|||
* Перезагрузка приложения |
|||
* Запуск нескольких экземпляров приложения |
|||
* Управление памятью |
|||
* Использование перечисленных функций перед запуском приложения |
|||
|
|||
В большинстве случаев Вам, вероятно, не нужно использовать какой-либо базовый образ, **лучше создать образ контейнера с нуля** на основе официального Docker-образа Python. |
|||
|
|||
Позаботившись о **порядке написания** инструкций в `Dockerfile`, Вы сможете использовать **кэш Docker'а**, **минимизировав время сборки**, максимально повысив свою производительность (и избежать скуки). 😎 |
|||
|
|||
В некоторых особых случаях вы можете использовать официальный образ Docker для FastAPI. 🤓 |
@ -0,0 +1,178 @@ |
|||
# Люди FastAPI |
|||
|
|||
FastAPI має дивовижну спільноту, яка вітає людей різного походження. |
|||
|
|||
## Творець – Супроводжувач |
|||
|
|||
Привіт! 👋 |
|||
|
|||
Це я: |
|||
|
|||
{% 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">Answers: {{ user.answers }}</div><div class="count">Pull Requests: {{ user.prs }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Я - творець і супроводжувач **FastAPI**. Детальніше про це можна прочитати в [Довідка FastAPI - Отримати довідку - Зв'язатися з автором](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. |
|||
|
|||
...Але тут я хочу показати вам спільноту. |
|||
|
|||
--- |
|||
|
|||
**FastAPI** отримує велику підтримку від спільноти. І я хочу відзначити їхній внесок. |
|||
|
|||
Це люди, які: |
|||
|
|||
* [Допомагають іншим із проблемами (запитаннями) у GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}. |
|||
* [Створюють пул реквести](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. |
|||
* Переглядають пул реквести, [особливо важливо для перекладів](contributing.md#translations){.internal-link target=_blank}. |
|||
|
|||
Оплески їм. 👏 🙇 |
|||
|
|||
## Найбільш активні користувачі минулого місяця |
|||
|
|||
Це користувачі, які [найбільше допомагали іншим із проблемами (запитаннями) у GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} протягом минулого місяця. ☕ |
|||
|
|||
{% 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 replied: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Експерти |
|||
|
|||
Ось **експерти FastAPI**. 🤓 |
|||
|
|||
Це користувачі, які [найбільше допомагали іншим із проблемами (запитаннями) у GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} протягом *всього часу*. |
|||
|
|||
Вони зарекомендували себе як експерти, допомагаючи багатьом іншим. ✨ |
|||
|
|||
{% 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 replied: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Найкращі контрибютори |
|||
|
|||
Ось **Найкращі контрибютори**. 👷 |
|||
|
|||
Ці користувачі [створили найбільшу кількість пул реквестів](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} які були *змержені*. |
|||
|
|||
Вони надали програмний код, документацію, переклади тощо. 📦 |
|||
|
|||
{% 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 %} |
|||
|
|||
Є багато інших контрибюторів (більше сотні), їх усіх можна побачити на сторінці <a href="https://github.com/tiangolo/fastapi/graphs/contributors" class="external-link" target="_blank">FastAPI GitHub Contributors</a>. 👷 |
|||
|
|||
## Найкращі рецензенти |
|||
|
|||
Ці користувачі є **Найкращими рецензентами**. 🕵️ |
|||
|
|||
### Рецензенти на переклади |
|||
|
|||
Я розмовляю лише кількома мовами (і не дуже добре 😅). Отже, рецензенти – це ті, хто має [**повноваження схвалювати переклади**](contributing.md#translations){.internal-link target=_blank} документації. Без них не було б документації кількома іншими мовами. |
|||
|
|||
--- |
|||
|
|||
**Найкращі рецензенти** 🕵️ переглянули більшість пул реквестів від інших, забезпечуючи якість коду, документації і особливо **перекладів**. |
|||
|
|||
{% 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">Reviews: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Спонсори |
|||
|
|||
Це **Спонсори**. 😎 |
|||
|
|||
Вони підтримують мою роботу з **FastAPI** (та іншими), переважно через <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>. |
|||
|
|||
{% if sponsors %} |
|||
|
|||
{% if sponsors.gold %} |
|||
|
|||
### Золоті спонсори |
|||
|
|||
{% 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 %} |
|||
|
|||
### Срібні спонсори |
|||
|
|||
{% 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 %} |
|||
|
|||
### Бронзові спонсори |
|||
|
|||
{% 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 %} |
|||
|
|||
### Індивідуальні спонсори |
|||
|
|||
{% 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 %} |
|||
|
|||
## Про дані - технічні деталі |
|||
|
|||
Основна мета цієї сторінки – висвітлити зусилля спільноти, щоб допомогти іншим. |
|||
|
|||
Особливо враховуючи зусилля, які зазвичай менш помітні, а в багатьох випадках більш важкі, як-от допомога іншим із проблемами та перегляд пул реквестів перекладів. |
|||
|
|||
Дані розраховуються щомісяця, ви можете ознайомитися з <a href="https://github.com/tiangolo/fastapi/blob/master/.github/actions/people/app/main.py" class="external-link" target="_blank">вихідним кодом тут</a>. |
|||
|
|||
Тут я також підкреслюю внески спонсорів. |
|||
|
|||
Я також залишаю за собою право оновлювати алгоритми підрахунку, види рейтингів, порогові значення тощо (про всяк випадок 🤷). |
@ -0,0 +1,96 @@ |
|||
# Параметри Cookie |
|||
|
|||
Ви можете визначити параметри Cookie таким же чином, як визначаються параметри `Query` і `Path`. |
|||
|
|||
## Імпорт `Cookie` |
|||
|
|||
Спочатку імпортуйте `Cookie`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ non-Annotated" |
|||
|
|||
!!! tip |
|||
Бажано використовувати `Annotated` версію, якщо це можливо. |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ non-Annotated" |
|||
|
|||
!!! tip |
|||
Бажано використовувати `Annotated` версію, якщо це можливо. |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/cookie_params/tutorial001.py!} |
|||
``` |
|||
|
|||
## Визначення параметрів `Cookie` |
|||
|
|||
Потім визначте параметри cookie, використовуючи таку ж конструкцію як для `Path` і `Query`. |
|||
|
|||
Перше значення це значення за замовчуванням, ви можете також передати всі додаткові параметри валідації чи анотації: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="10" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ non-Annotated" |
|||
|
|||
!!! tip |
|||
Бажано використовувати `Annotated` версію, якщо це можливо. |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ non-Annotated" |
|||
|
|||
!!! tip |
|||
Бажано використовувати `Annotated` версію, якщо це можливо. |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/cookie_params/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "Технічні Деталі" |
|||
`Cookie` це "сестра" класів `Path` і `Query`. Вони наслідуються від одного батьківського класу `Param`. |
|||
Але пам'ятайте, що коли ви імпортуєте `Query`, `Path`, `Cookie` та інше з `fastapi`, це фактично функції, що повертають спеціальні класи. |
|||
|
|||
!!! info |
|||
Для визначення cookies ви маєте використовувати `Cookie`, тому що в іншому випадку параметри будуть інтерпритовані, як параметри запиту. |
|||
|
|||
## Підсумки |
|||
|
|||
Визначайте cookies за допомогою `Cookie`, використовуючи той же спільний шаблон, що і `Query` та `Path`. |
@ -0,0 +1,545 @@ |
|||
# Giới thiệu kiểu dữ liệu Python |
|||
|
|||
Python hỗ trợ tùy chọn "type hints" (còn được gọi là "type annotations"). |
|||
|
|||
Những **"type hints"** hay chú thích là một cú pháp đặc biệt cho phép khai báo <abbr title="ví dụ: str, int, float, bool"> kiểu dữ liệu</abbr> của một biến. |
|||
|
|||
Bằng việc khai báo kiểu dữ liệu cho các biến của bạn, các trình soạn thảo và các công cụ có thể hỗ trợ bạn tốt hơn. |
|||
|
|||
Đây chỉ là một **hướng dẫn nhanh** về gợi ý kiểu dữ liệu trong Python. Nó chỉ bao gồm những điều cần thiết tối thiểu để sử dụng chúng với **FastAPI**... đó thực sự là rất ít. |
|||
|
|||
**FastAPI** hoàn toàn được dựa trên những gợi ý kiểu dữ liệu, chúng mang đến nhiều ưu điểm và lợi ích. |
|||
|
|||
Nhưng thậm chí nếu bạn không bao giờ sử dụng **FastAPI**, bạn sẽ được lợi từ việc học một ít về chúng. |
|||
|
|||
!!! note |
|||
Nếu bạn là một chuyên gia về Python, và bạn đã biết mọi thứ về gợi ý kiểu dữ liệu, bỏ qua và đi tới chương tiếp theo. |
|||
|
|||
## Động lực |
|||
|
|||
Hãy bắt đầu với một ví dụ đơn giản: |
|||
|
|||
```Python |
|||
{!../../../docs_src/python_types/tutorial001.py!} |
|||
``` |
|||
|
|||
Kết quả khi gọi chương trình này: |
|||
|
|||
``` |
|||
John Doe |
|||
``` |
|||
|
|||
Hàm thực hiện như sau: |
|||
|
|||
* Lấy một `first_name` và `last_name`. |
|||
* Chuyển đổi kí tự đầu tiên của mỗi biến sang kiểu chữ hoa với `title()`. |
|||
* <abbr title="Đặt chúng lại với nhau thành một. Với các nội dung lần lượt.">Nối</abbr> chúng lại với nhau bằng một kí tự trắng ở giữa. |
|||
|
|||
```Python hl_lines="2" |
|||
{!../../../docs_src/python_types/tutorial001.py!} |
|||
``` |
|||
|
|||
### Sửa đổi |
|||
|
|||
Nó là một chương trình rất đơn giản. |
|||
|
|||
Nhưng bây giờ hình dung rằng bạn đang viết nó từ đầu. |
|||
|
|||
Tại một vài thời điểm, bạn sẽ bắt đầu định nghĩa hàm, bạn có các tham số... |
|||
|
|||
Nhưng sau đó bạn phải gọi "phương thức chuyển đổi kí tự đầu tiên sang kiểu chữ hoa". |
|||
|
|||
Có phải là `upper`? Có phải là `uppercase`? `first_uppercase`? `capitalize`? |
|||
|
|||
Sau đó, bạn thử hỏi người bạn cũ của mình, autocompletion của trình soạn thảo. |
|||
|
|||
Bạn gõ tham số đầu tiên của hàm, `first_name`, sau đó một dấu chấm (`.`) và sau đó ấn `Ctrl+Space` để kích hoạt bộ hoàn thành. |
|||
|
|||
Nhưng đáng buồn, bạn không nhận được điều gì hữu ích cả: |
|||
|
|||
<img src="/img/python-types/image01.png"> |
|||
|
|||
### Thêm kiểu dữ liệu |
|||
|
|||
Hãy sửa một dòng từ phiên bản trước. |
|||
|
|||
Chúng ta sẽ thay đổi chính xác đoạn này, tham số của hàm, từ: |
|||
|
|||
```Python |
|||
first_name, last_name |
|||
``` |
|||
|
|||
sang: |
|||
|
|||
```Python |
|||
first_name: str, last_name: str |
|||
``` |
|||
|
|||
Chính là nó. |
|||
|
|||
Những thứ đó là "type hints": |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial002.py!} |
|||
``` |
|||
|
|||
Đó không giống như khai báo những giá trị mặc định giống như: |
|||
|
|||
```Python |
|||
first_name="john", last_name="doe" |
|||
``` |
|||
|
|||
Nó là một thứ khác. |
|||
|
|||
Chúng ta sử dụng dấu hai chấm (`:`), không phải dấu bằng (`=`). |
|||
|
|||
Và việc thêm gợi ý kiểu dữ liệu không làm thay đổi những gì xảy ra so với khi chưa thêm chúng. |
|||
|
|||
But now, imagine you are again in the middle of creating that function, but with type hints. |
|||
|
|||
Tại cùng một điểm, bạn thử kích hoạt autocomplete với `Ctrl+Space` và bạn thấy: |
|||
|
|||
<img src="/img/python-types/image02.png"> |
|||
|
|||
Với cái đó, bạn có thể cuộn, nhìn thấy các lựa chọn, cho đến khi bạn tìm thấy một "tiếng chuông": |
|||
|
|||
<img src="/img/python-types/image03.png"> |
|||
|
|||
## Động lực nhiều hơn |
|||
|
|||
Kiểm tra hàm này, nó đã có gợi ý kiểu dữ liệu: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial003.py!} |
|||
``` |
|||
|
|||
Bởi vì trình soạn thảo biết kiểu dữ liệu của các biến, bạn không chỉ có được completion, bạn cũng được kiểm tra lỗi: |
|||
|
|||
<img src="/img/python-types/image04.png"> |
|||
|
|||
Bây giờ bạn biết rằng bạn phải sửa nó, chuyển `age` sang một xâu với `str(age)`: |
|||
|
|||
```Python hl_lines="2" |
|||
{!../../../docs_src/python_types/tutorial004.py!} |
|||
``` |
|||
|
|||
## Khai báo các kiểu dữ liệu |
|||
|
|||
Bạn mới chỉ nhìn thấy những nơi chủ yếu để đặt khai báo kiểu dữ liệu. Như là các tham số của hàm. |
|||
|
|||
Đây cũng là nơi chủ yếu để bạn sử dụng chúng với **FastAPI**. |
|||
|
|||
### Kiểu dữ liệu đơn giản |
|||
|
|||
Bạn có thể khai báo tất cả các kiểu dữ liệu chuẩn của Python, không chỉ là `str`. |
|||
|
|||
Bạn có thể sử dụng, ví dụ: |
|||
|
|||
* `int` |
|||
* `float` |
|||
* `bool` |
|||
* `bytes` |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial005.py!} |
|||
``` |
|||
|
|||
### Các kiểu dữ liệu tổng quát với tham số kiểu dữ liệu |
|||
|
|||
Có một vài cấu trúc dữ liệu có thể chứa các giá trị khác nhau như `dict`, `list`, `set` và `tuple`. Và những giá trị nội tại cũng có thể có kiểu dữ liệu của chúng. |
|||
|
|||
Những kiểu dữ liệu nội bộ này được gọi là những kiểu dữ liệu "**tổng quát**". Và có khả năng khai báo chúng, thậm chí với các kiểu dữ liệu nội bộ của chúng. |
|||
|
|||
Để khai báo những kiểu dữ liệu và những kiểu dữ liệu nội bộ đó, bạn có thể sử dụng mô đun chuẩn của Python là `typing`. Nó có hỗ trợ những gợi ý kiểu dữ liệu này. |
|||
|
|||
#### Những phiên bản mới hơn của Python |
|||
|
|||
Cú pháp sử dụng `typing` **tương thích** với tất cả các phiên bản, từ Python 3.6 tới những phiên bản cuối cùng, bao gồm Python 3.9, Python 3.10,... |
|||
|
|||
As Python advances, **những phiên bản mới** mang tới sự hỗ trợ được cải tiến cho những chú thích kiểu dữ liệu và trong nhiều trường hợp bạn thậm chí sẽ không cần import và sử dụng mô đun `typing` để khai báo chú thích kiểu dữ liệu. |
|||
|
|||
Nếu bạn có thể chọn một phiên bản Python gần đây hơn cho dự án của bạn, ban sẽ có được những ưu điểm của những cải tiến đơn giản đó. |
|||
|
|||
Trong tất cả các tài liệu tồn tại những ví dụ tương thích với mỗi phiên bản Python (khi có một sự khác nhau). |
|||
|
|||
Cho ví dụ "**Python 3.6+**" có nghĩa là nó tương thích với Python 3.7 hoặc lớn hơn (bao gồm 3.7, 3.8, 3.9, 3.10,...). và "**Python 3.9+**" nghĩa là nó tương thích với Python 3.9 trở lên (bao gồm 3.10,...). |
|||
|
|||
Nếu bạn có thể sử dụng **phiên bản cuối cùng của Python**, sử dụng những ví dụ cho phiên bản cuối, những cái đó sẽ có **cú pháp đơn giản và tốt nhât**, ví dụ, "**Python 3.10+**". |
|||
|
|||
#### List |
|||
|
|||
Ví dụ, hãy định nghĩa một biến là `list` các `str`. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
Khai báo biến với cùng dấu hai chấm (`:`). |
|||
|
|||
Tương tự kiểu dữ liệu `list`. |
|||
|
|||
Như danh sách là một kiểu dữ liệu chứa một vài kiểu dữ liệu có sẵn, bạn đặt chúng trong các dấu ngoặc vuông: |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/python_types/tutorial006_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
Từ `typing`, import `List` (với chữ cái `L` viết hoa): |
|||
|
|||
``` Python hl_lines="1" |
|||
{!> ../../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
Khai báo biến với cùng dấu hai chấm (`:`). |
|||
|
|||
Tương tự như kiểu dữ liệu, `List` bạn import từ `typing`. |
|||
|
|||
Như danh sách là một kiểu dữ liệu chứa các kiểu dữ liệu có sẵn, bạn đặt chúng bên trong dấu ngoặc vuông: |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
!!! info |
|||
Các kiểu dữ liệu có sẵn bên trong dấu ngoặc vuông được gọi là "tham số kiểu dữ liệu". |
|||
|
|||
Trong trường hợp này, `str` là tham số kiểu dữ liệu được truyền tới `List` (hoặc `list` trong Python 3.9 trở lên). |
|||
|
|||
Có nghĩa là: "biến `items` là một `list`, và mỗi phần tử trong danh sách này là một `str`". |
|||
|
|||
!!! tip |
|||
Nếu bạn sử dụng Python 3.9 hoặc lớn hơn, bạn không phải import `List` từ `typing`, bạn có thể sử dụng `list` để thay thế. |
|||
|
|||
Bằng cách này, trình soạn thảo của bạn có thể hỗ trợ trong khi xử lí các phần tử trong danh sách: |
|||
|
|||
<img src="/img/python-types/image05.png"> |
|||
|
|||
Đa phần đều không thể đạt được nếu không có các kiểu dữ liệu. |
|||
|
|||
Chú ý rằng, biến `item` là một trong các phần tử trong danh sách `items`. |
|||
|
|||
Và do vậy, trình soạn thảo biết nó là một `str`, và cung cấp sự hỗ trợ cho nó. |
|||
|
|||
#### Tuple and Set |
|||
|
|||
Bạn sẽ làm điều tương tự để khai báo các `tuple` và các `set`: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/python_types/tutorial007_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial007.py!} |
|||
``` |
|||
|
|||
Điều này có nghĩa là: |
|||
|
|||
* Biến `items_t` là một `tuple` với 3 phần tử, một `int`, một `int` nữa, và một `str`. |
|||
* Biến `items_s` là một `set`, và mỗi phần tử của nó có kiểu `bytes`. |
|||
|
|||
#### Dict |
|||
|
|||
Để định nghĩa một `dict`, bạn truyền 2 tham số kiểu dữ liệu, phân cách bởi dấu phẩy. |
|||
|
|||
Tham số kiểu dữ liệu đầu tiên dành cho khóa của `dict`. |
|||
|
|||
Tham số kiểu dữ liệu thứ hai dành cho giá trị của `dict`. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/python_types/tutorial008_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial008.py!} |
|||
``` |
|||
|
|||
Điều này có nghĩa là: |
|||
|
|||
* Biến `prices` là một `dict`: |
|||
* Khóa của `dict` này là kiểu `str` (đó là tên của mỗi vật phẩm). |
|||
* Giá trị của `dict` này là kiểu `float` (đó là giá của mỗi vật phẩm). |
|||
|
|||
#### Union |
|||
|
|||
Bạn có thể khai báo rằng một biến có thể là **một vài kiểu dữ liệu" bất kì, ví dụ, một `int` hoặc một `str`. |
|||
|
|||
Trong Python 3.6 hoặc lớn hơn (bao gồm Python 3.10) bạn có thể sử dụng kiểu `Union` từ `typing` và đặt trong dấu ngoặc vuông những giá trị được chấp nhận. |
|||
|
|||
In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr>. |
|||
|
|||
Trong Python 3.10 cũng có một **cú pháp mới** mà bạn có thể đặt những kiểu giá trị khả thi phân cách bởi một dấu <abbr title='cũng được gọi là "toán tử nhị phân"'>sổ dọc (`|`)</abbr>. |
|||
|
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/python_types/tutorial008b_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial008b.py!} |
|||
``` |
|||
|
|||
Trong cả hai trường hợp có nghĩa là `item` có thể là một `int` hoặc `str`. |
|||
|
|||
#### Khả năng `None` |
|||
|
|||
Bạn có thể khai báo một giá trị có thể có một kiểu dữ liệu, giống như `str`, nhưng nó cũng có thể là `None`. |
|||
|
|||
Trong Python 3.6 hoặc lớn hơn (bao gồm Python 3.10) bạn có thể khai báo nó bằng các import và sử dụng `Optional` từ mô đun `typing`. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial009.py!} |
|||
``` |
|||
|
|||
Sử dụng `Optional[str]` thay cho `str` sẽ cho phép trình soạn thảo giúp bạn phát hiện các lỗi mà bạn có thể gặp như một giá trị luôn là một `str`, trong khi thực tế nó rất có thể là `None`. |
|||
|
|||
`Optional[Something]` là một cách viết ngắn gọn của `Union[Something, None]`, chúng là tương đương nhau. |
|||
|
|||
Điều này cũng có nghĩa là trong Python 3.10, bạn có thể sử dụng `Something | None`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/python_types/tutorial009_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial009.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ alternative" |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial009b.py!} |
|||
``` |
|||
|
|||
#### Sử dụng `Union` hay `Optional` |
|||
|
|||
If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view: |
|||
|
|||
Nếu bạn đang sử dụng phiên bản Python dưới 3.10, đây là một mẹo từ ý kiến rất "chủ quan" của tôi: |
|||
|
|||
* 🚨 Tránh sử dụng `Optional[SomeType]` |
|||
* Thay vào đó ✨ **sử dụng `Union[SomeType, None]`** ✨. |
|||
|
|||
Cả hai là tương đương và bên dưới chúng giống nhau, nhưng tôi sẽ đễ xuất `Union` thay cho `Optional` vì từ "**tùy chọn**" có vẻ ngầm định giá trị là tùy chọn, và nó thực sự có nghĩa rằng "nó có thể là `None`", do đó nó không phải là tùy chọn và nó vẫn được yêu cầu. |
|||
|
|||
Tôi nghĩ `Union[SomeType, None]` là rõ ràng hơn về ý nghĩa của nó. |
|||
|
|||
Nó chỉ là về các từ và tên. Nhưng những từ đó có thể ảnh hưởng cách bạn và những đồng đội của bạn suy nghĩ về code. |
|||
|
|||
Cho một ví dụ, hãy để ý hàm này: |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial009c.py!} |
|||
``` |
|||
|
|||
Tham số `name` được định nghĩa là `Optional[str]`, nhưng nó **không phải là tùy chọn**, bạn không thể gọi hàm mà không có tham số: |
|||
|
|||
```Python |
|||
say_hi() # Oh, no, this throws an error! 😱 |
|||
``` |
|||
|
|||
Tham số `name` **vẫn được yêu cầu** (không phải là *tùy chọn*) vì nó không có giá trị mặc định. Trong khi đó, `name` chấp nhận `None` như là giá trị: |
|||
|
|||
```Python |
|||
say_hi(name=None) # This works, None is valid 🎉 |
|||
``` |
|||
|
|||
Tin tốt là, khi bạn sử dụng Python 3.10, bạn sẽ không phải lo lắng về điều đó, bạn sẽ có thể sử dụng `|` để định nghĩa hợp của các kiểu dữ liệu một cách đơn giản: |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial009c_py310.py!} |
|||
``` |
|||
|
|||
Và sau đó, bạn sẽ không phải lo rằng những cái tên như `Optional` và `Union`. 😎 |
|||
|
|||
|
|||
#### Những kiểu dữ liệu tổng quát |
|||
|
|||
Những kiểu dữ liệu này lấy tham số kiểu dữ liệu trong dấu ngoặc vuông được gọi là **Kiểu dữ liệu tổng quát**, cho ví dụ: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
Bạn có thể sử dụng các kiểu dữ liệu có sẵn như là kiểu dữ liệu tổng quát (với ngoặc vuông và kiểu dữ liệu bên trong): |
|||
|
|||
* `list` |
|||
* `tuple` |
|||
* `set` |
|||
* `dict` |
|||
|
|||
Và tương tự với Python 3.6, từ mô đun `typing`: |
|||
|
|||
* `Union` |
|||
* `Optional` (tương tự như Python 3.6) |
|||
* ...và các kiểu dữ liệu khác. |
|||
|
|||
Trong Python 3.10, thay vì sử dụng `Union` và `Optional`, bạn có thể sử dụng <abbr title='cũng gọi là "toán tử nhị phân", nhưng ý nghĩa không liên quan ở đây'>sổ dọc ('|')</abbr> để khai báo hợp của các kiểu dữ liệu, điều đó tốt hơn và đơn giản hơn nhiều. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
Bạn có thể sử dụng các kiểu dữ liệu có sẵn tương tự như (với ngoặc vuông và kiểu dữ liệu bên trong): |
|||
|
|||
* `list` |
|||
* `tuple` |
|||
* `set` |
|||
* `dict` |
|||
|
|||
Và tương tự với Python 3.6, từ mô đun `typing`: |
|||
|
|||
* `Union` |
|||
* `Optional` |
|||
* ...and others. |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
* `List` |
|||
* `Tuple` |
|||
* `Set` |
|||
* `Dict` |
|||
* `Union` |
|||
* `Optional` |
|||
* ...và các kiểu khác. |
|||
|
|||
### Lớp như kiểu dữ liệu |
|||
|
|||
Bạn cũng có thể khai báo một lớp như là kiểu dữ liệu của một biến. |
|||
|
|||
Hãy nói rằng bạn muốn có một lớp `Person` với một tên: |
|||
|
|||
```Python hl_lines="1-3" |
|||
{!../../../docs_src/python_types/tutorial010.py!} |
|||
``` |
|||
|
|||
Sau đó bạn có thể khai báo một biến có kiểu là `Person`: |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/python_types/tutorial010.py!} |
|||
``` |
|||
|
|||
Và lại một lần nữa, bạn có được tất cả sự hỗ trợ từ trình soạn thảo: |
|||
|
|||
<img src="/img/python-types/image06.png"> |
|||
|
|||
Lưu ý rằng, điều này có nghĩa rằng "`one_person`" là một **thực thể** của lớp `Person`. |
|||
|
|||
Nó không có nghĩa "`one_person`" là một **lớp** gọi là `Person`. |
|||
|
|||
## Pydantic models |
|||
|
|||
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> là một thư viện Python để validate dữ liệu hiệu năng cao. |
|||
|
|||
Bạn có thể khai báo "hình dạng" của dữa liệu như là các lớp với các thuộc tính. |
|||
|
|||
Và mỗi thuộc tính có một kiểu dữ liệu. |
|||
|
|||
Sau đó bạn tạo một thực thể của lớp đó với một vài giá trị và nó sẽ validate các giá trị, chuyển đổi chúng sang kiểu dữ liệu phù hợp (nếu đó là trường hợp) và cho bạn một object với toàn bộ dữ liệu. |
|||
|
|||
Và bạn nhận được tất cả sự hỗ trợ của trình soạn thảo với object kết quả đó. |
|||
|
|||
Một ví dụ từ tài liệu chính thức của Pydantic: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/python_types/tutorial011_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/python_types/tutorial011_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/python_types/tutorial011.py!} |
|||
``` |
|||
|
|||
!!! info |
|||
Để học nhiều hơn về <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic, tham khảo tài liệu của nó</a>. |
|||
|
|||
**FastAPI** được dựa hoàn toàn trên Pydantic. |
|||
|
|||
Bạn sẽ thấy nhiều ví dụ thực tế hơn trong [Hướng dẫn sử dụng](tutorial/index.md){.internal-link target=_blank}. |
|||
|
|||
!!! tip |
|||
Pydantic có một hành vi đặc biệt khi bạn sử dụng `Optional` hoặc `Union[Something, None]` mà không có giá trị mặc dịnh, bạn có thể đọc nhiều hơn về nó trong tài liệu của Pydantic về <a href="https://pydantic-docs.helpmanual.io/usage/models/#required-optional-fields" class="external-link" target="_blank">Required Optional fields</a>. |
|||
|
|||
|
|||
## Type Hints với Metadata Annotations |
|||
|
|||
Python cũng có một tính năng cho phép đặt **metadata bổ sung** trong những gợi ý kiểu dữ liệu này bằng cách sử dụng `Annotated`. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
Trong Python 3.9, `Annotated` là một phần của thư viện chuẩn, do đó bạn có thể import nó từ `typing`. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial013_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
Ở phiên bản dưới Python 3.9, bạn import `Annotated` từ `typing_extensions`. |
|||
|
|||
Nó đã được cài đặt sẵng cùng với **FastAPI**. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../../docs_src/python_types/tutorial013.py!} |
|||
``` |
|||
|
|||
Python bản thân nó không làm bất kì điều gì với `Annotated`. Với các trình soạn thảo và các công cụ khác, kiểu dữ liệu vẫn là `str`. |
|||
|
|||
Nhưng bạn có thể sử dụng `Annotated` để cung cấp cho **FastAPI** metadata bổ sung về cách mà bạn muốn ứng dụng của bạn xử lí. |
|||
|
|||
Điều quan trọng cần nhớ là ***tham số kiểu dữ liệu* đầu tiên** bạn truyền tới `Annotated` là **kiểu giá trị thực sự**. Phần còn lại chỉ là metadata cho các công cụ khác. |
|||
|
|||
Bây giờ, bạn chỉ cần biết rằng `Annotated` tồn tại, và nó là tiêu chuẩn của Python. 😎 |
|||
|
|||
|
|||
Sau đó, bạn sẽ thấy sự **mạnh mẽ** mà nó có thể làm. |
|||
|
|||
!!! tip |
|||
Thực tế, cái này là **tiêu chuẩn của Python**, nghĩa là bạn vẫn sẽ có được **trải nghiệm phát triển tốt nhất có thể** với trình soạn thảo của bạn, với các công cụ bạn sử dụng để phân tích và tái cấu trúc code của bạn, etc. ✨ |
|||
|
|||
Và code của bạn sẽ tương thích với nhiều công cụ và thư viện khác của Python. 🚀 |
|||
|
|||
## Các gợi ý kiểu dữ liệu trong **FastAPI** |
|||
|
|||
**FastAPI** lấy các ưu điểm của các gợi ý kiểu dữ liệu để thực hiện một số thứ. |
|||
|
|||
Với **FastAPI**, bạn khai báo các tham số với gợi ý kiểu và bạn có được: |
|||
|
|||
* **Sự hỗ trợ từ các trình soạn thảo**. |
|||
* **Kiểm tra kiểu dữ liệu (type checking)**. |
|||
|
|||
...và **FastAPI** sử dụng các khia báo để: |
|||
|
|||
* **Định nghĩa các yêu cầu**: từ tham số đường dẫn của request, tham số query, headers, bodies, các phụ thuộc (dependencies),... |
|||
* **Chuyển dổi dữ liệu*: từ request sang kiểu dữ liệu được yêu cầu. |
|||
* **Kiểm tra tính đúng đắn của dữ liệu**: tới từ mỗi request: |
|||
* Sinh **lỗi tự động** để trả về máy khác khi dữ liệu không hợp lệ. |
|||
* **Tài liệu hóa** API sử dụng OpenAPI: |
|||
* cái mà sau được được sử dụng bởi tài liệu tương tác người dùng. |
|||
|
|||
Điều này có thể nghe trừu tượng. Đừng lo lắng. Bạn sẽ thấy tất cả chúng trong [Hướng dẫn sử dụng](tutorial/index.md){.internal-link target=_blank}. |
|||
|
|||
Điều quan trọng là bằng việc sử dụng các kiểu dữ liệu chuẩn của Python (thay vì thêm các lớp, decorators,...), **FastAPI** sẽ thực hiện nhiều công việc cho bạn. |
|||
|
|||
!!! info |
|||
Nếu bạn đã đi qua toàn bộ các hướng dẫn và quay trở lại để tìm hiểu nhiều hơn về các kiểu dữ liệu, một tài nguyên tốt như <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">"cheat sheet" từ `mypy`</a>. |
@ -0,0 +1,38 @@ |
|||
from fastapi import FastAPI |
|||
from fastapi.openapi.docs import ( |
|||
get_redoc_html, |
|||
get_swagger_ui_html, |
|||
get_swagger_ui_oauth2_redirect_html, |
|||
) |
|||
|
|||
app = FastAPI(docs_url=None, redoc_url=None) |
|||
|
|||
|
|||
@app.get("/docs", include_in_schema=False) |
|||
async def custom_swagger_ui_html(): |
|||
return get_swagger_ui_html( |
|||
openapi_url=app.openapi_url, |
|||
title=app.title + " - Swagger UI", |
|||
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, |
|||
swagger_js_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js", |
|||
swagger_css_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css", |
|||
) |
|||
|
|||
|
|||
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False) |
|||
async def swagger_ui_redirect(): |
|||
return get_swagger_ui_oauth2_redirect_html() |
|||
|
|||
|
|||
@app.get("/redoc", include_in_schema=False) |
|||
async def redoc_html(): |
|||
return get_redoc_html( |
|||
openapi_url=app.openapi_url, |
|||
title=app.title + " - ReDoc", |
|||
redoc_js_url="https://unpkg.com/redoc@next/bundles/redoc.standalone.js", |
|||
) |
|||
|
|||
|
|||
@app.get("/users/{username}") |
|||
async def read_user(username: str): |
|||
return {"message": f"Hello {username}"} |
@ -1,6 +1,6 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from docs_src.extending_openapi.tutorial003 import app |
|||
from docs_src.configure_swagger_ui.tutorial001 import app |
|||
|
|||
client = TestClient(app) |
|||
|
@ -1,6 +1,6 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from docs_src.extending_openapi.tutorial004 import app |
|||
from docs_src.configure_swagger_ui.tutorial002 import app |
|||
|
|||
client = TestClient(app) |
|||
|
@ -1,6 +1,6 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from docs_src.extending_openapi.tutorial005 import app |
|||
from docs_src.configure_swagger_ui.tutorial003 import app |
|||
|
|||
client = TestClient(app) |
|||
|
@ -0,0 +1,42 @@ |
|||
import os |
|||
from pathlib import Path |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
|
|||
|
|||
@pytest.fixture(scope="module") |
|||
def client(): |
|||
static_dir: Path = Path(os.getcwd()) / "static" |
|||
print(static_dir) |
|||
static_dir.mkdir(exist_ok=True) |
|||
from docs_src.custom_docs_ui.tutorial001 import app |
|||
|
|||
with TestClient(app) as client: |
|||
yield client |
|||
static_dir.rmdir() |
|||
|
|||
|
|||
def test_swagger_ui_html(client: TestClient): |
|||
response = client.get("/docs") |
|||
assert response.status_code == 200, response.text |
|||
assert "https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js" in response.text |
|||
assert "https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" in response.text |
|||
|
|||
|
|||
def test_swagger_ui_oauth2_redirect_html(client: TestClient): |
|||
response = client.get("/docs/oauth2-redirect") |
|||
assert response.status_code == 200, response.text |
|||
assert "window.opener.swaggerUIRedirectOauth2" in response.text |
|||
|
|||
|
|||
def test_redoc_html(client: TestClient): |
|||
response = client.get("/redoc") |
|||
assert response.status_code == 200, response.text |
|||
assert "https://unpkg.com/redoc@next/bundles/redoc.standalone.js" in response.text |
|||
|
|||
|
|||
def test_api(client: TestClient): |
|||
response = client.get("/users/john") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json()["message"] == "Hello john" |
@ -1,454 +0,0 @@ |
|||
import time |
|||
from pathlib import Path |
|||
from unittest.mock import MagicMock |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from ...utils import needs_pydanticv1 |
|||
|
|||
|
|||
@pytest.fixture(scope="module") |
|||
def client(): |
|||
# Import while creating the client to create the DB after starting the test session |
|||
from docs_src.sql_databases_peewee.sql_app.main import app |
|||
|
|||
test_db = Path("./test.db") |
|||
with TestClient(app) as c: |
|||
yield c |
|||
test_db.unlink() |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_create_user(client): |
|||
test_user = {"email": "[email protected]", "password": "secret"} |
|||
response = client.post("/users/", json=test_user) |
|||
assert response.status_code == 200, response.text |
|||
data = response.json() |
|||
assert test_user["email"] == data["email"] |
|||
assert "id" in data |
|||
response = client.post("/users/", json=test_user) |
|||
assert response.status_code == 400, response.text |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_get_user(client): |
|||
response = client.get("/users/1") |
|||
assert response.status_code == 200, response.text |
|||
data = response.json() |
|||
assert "email" in data |
|||
assert "id" in data |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_inexistent_user(client): |
|||
response = client.get("/users/999") |
|||
assert response.status_code == 404, response.text |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_get_users(client): |
|||
response = client.get("/users/") |
|||
assert response.status_code == 200, response.text |
|||
data = response.json() |
|||
assert "email" in data[0] |
|||
assert "id" in data[0] |
|||
|
|||
|
|||
time.sleep = MagicMock() |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_get_slowusers(client): |
|||
response = client.get("/slowusers/") |
|||
assert response.status_code == 200, response.text |
|||
data = response.json() |
|||
assert "email" in data[0] |
|||
assert "id" in data[0] |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_create_item(client): |
|||
item = {"title": "Foo", "description": "Something that fights"} |
|||
response = client.post("/users/1/items/", json=item) |
|||
assert response.status_code == 200, response.text |
|||
item_data = response.json() |
|||
assert item["title"] == item_data["title"] |
|||
assert item["description"] == item_data["description"] |
|||
assert "id" in item_data |
|||
assert "owner_id" in item_data |
|||
response = client.get("/users/1") |
|||
assert response.status_code == 200, response.text |
|||
user_data = response.json() |
|||
item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] |
|||
assert item_to_check["title"] == item["title"] |
|||
assert item_to_check["description"] == item["description"] |
|||
response = client.get("/users/1") |
|||
assert response.status_code == 200, response.text |
|||
user_data = response.json() |
|||
item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] |
|||
assert item_to_check["title"] == item["title"] |
|||
assert item_to_check["description"] == item["description"] |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_read_items(client): |
|||
response = client.get("/items/") |
|||
assert response.status_code == 200, response.text |
|||
data = response.json() |
|||
assert data |
|||
first_item = data[0] |
|||
assert "title" in first_item |
|||
assert "description" in first_item |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_openapi_schema(client): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/users/": { |
|||
"get": { |
|||
"summary": "Read Users", |
|||
"operationId": "read_users_users__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": { |
|||
"title": "Skip", |
|||
"type": "integer", |
|||
"default": 0, |
|||
}, |
|||
"name": "skip", |
|||
"in": "query", |
|||
}, |
|||
{ |
|||
"required": False, |
|||
"schema": { |
|||
"title": "Limit", |
|||
"type": "integer", |
|||
"default": 100, |
|||
}, |
|||
"name": "limit", |
|||
"in": "query", |
|||
}, |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"title": "Response Read Users Users Get", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/User"}, |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
"post": { |
|||
"summary": "Create User", |
|||
"operationId": "create_user_users__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/UserCreate"} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/User"} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
"/users/{user_id}": { |
|||
"get": { |
|||
"summary": "Read User", |
|||
"operationId": "read_user_users__user_id__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": True, |
|||
"schema": {"title": "User Id", "type": "integer"}, |
|||
"name": "user_id", |
|||
"in": "path", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/User"} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/users/{user_id}/items/": { |
|||
"post": { |
|||
"summary": "Create Item For User", |
|||
"operationId": "create_item_for_user_users__user_id__items__post", |
|||
"parameters": [ |
|||
{ |
|||
"required": True, |
|||
"schema": {"title": "User Id", "type": "integer"}, |
|||
"name": "user_id", |
|||
"in": "path", |
|||
} |
|||
], |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/ItemCreate"} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Item"} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/items/": { |
|||
"get": { |
|||
"summary": "Read Items", |
|||
"operationId": "read_items_items__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": { |
|||
"title": "Skip", |
|||
"type": "integer", |
|||
"default": 0, |
|||
}, |
|||
"name": "skip", |
|||
"in": "query", |
|||
}, |
|||
{ |
|||
"required": False, |
|||
"schema": { |
|||
"title": "Limit", |
|||
"type": "integer", |
|||
"default": 100, |
|||
}, |
|||
"name": "limit", |
|||
"in": "query", |
|||
}, |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"title": "Response Read Items Items Get", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/Item"}, |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/slowusers/": { |
|||
"get": { |
|||
"summary": "Read Slow Users", |
|||
"operationId": "read_slow_users_slowusers__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": { |
|||
"title": "Skip", |
|||
"type": "integer", |
|||
"default": 0, |
|||
}, |
|||
"name": "skip", |
|||
"in": "query", |
|||
}, |
|||
{ |
|||
"required": False, |
|||
"schema": { |
|||
"title": "Limit", |
|||
"type": "integer", |
|||
"default": 100, |
|||
}, |
|||
"name": "limit", |
|||
"in": "query", |
|||
}, |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"title": "Response Read Slow Users Slowusers Get", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/User"}, |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"HTTPValidationError": { |
|||
"title": "HTTPValidationError", |
|||
"type": "object", |
|||
"properties": { |
|||
"detail": { |
|||
"title": "Detail", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|||
} |
|||
}, |
|||
}, |
|||
"Item": { |
|||
"title": "Item", |
|||
"required": ["title", "id", "owner_id"], |
|||
"type": "object", |
|||
"properties": { |
|||
"title": {"title": "Title", "type": "string"}, |
|||
"description": {"title": "Description", "type": "string"}, |
|||
"id": {"title": "Id", "type": "integer"}, |
|||
"owner_id": {"title": "Owner Id", "type": "integer"}, |
|||
}, |
|||
}, |
|||
"ItemCreate": { |
|||
"title": "ItemCreate", |
|||
"required": ["title"], |
|||
"type": "object", |
|||
"properties": { |
|||
"title": {"title": "Title", "type": "string"}, |
|||
"description": {"title": "Description", "type": "string"}, |
|||
}, |
|||
}, |
|||
"User": { |
|||
"title": "User", |
|||
"required": ["email", "id", "is_active"], |
|||
"type": "object", |
|||
"properties": { |
|||
"email": {"title": "Email", "type": "string"}, |
|||
"id": {"title": "Id", "type": "integer"}, |
|||
"is_active": {"title": "Is Active", "type": "boolean"}, |
|||
"items": { |
|||
"title": "Items", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/Item"}, |
|||
"default": [], |
|||
}, |
|||
}, |
|||
}, |
|||
"UserCreate": { |
|||
"title": "UserCreate", |
|||
"required": ["email", "password"], |
|||
"type": "object", |
|||
"properties": { |
|||
"email": {"title": "Email", "type": "string"}, |
|||
"password": {"title": "Password", "type": "string"}, |
|||
}, |
|||
}, |
|||
"ValidationError": { |
|||
"title": "ValidationError", |
|||
"required": ["loc", "msg", "type"], |
|||
"type": "object", |
|||
"properties": { |
|||
"loc": { |
|||
"title": "Location", |
|||
"type": "array", |
|||
"items": { |
|||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|||
}, |
|||
}, |
|||
"msg": {"title": "Message", "type": "string"}, |
|||
"type": {"title": "Error Type", "type": "string"}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
} |
Loading…
Reference in new issue